wimlib-1.13.1/0000755000175000017500000000000013464166632010074 500000000000000wimlib-1.13.1/src/0000755000175000017500000000000013464166632010663 500000000000000wimlib-1.13.1/src/inode_table.c0000644000175000017500000001332513160354224013205 00000000000000/* * inode_table.c - hard link detection */ /* * Copyright (C) 2012-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib/bitops.h" #include "wimlib/dentry.h" #include "wimlib/error.h" #include "wimlib/inode.h" #include "wimlib/inode_table.h" #include "wimlib/list.h" #include "wimlib/util.h" /* Initialize a hash table for hard link detection. */ int init_inode_table(struct wim_inode_table *table, size_t capacity) { capacity = roundup_pow_of_2(capacity); table->array = CALLOC(capacity, sizeof(table->array[0])); if (!table->array) return WIMLIB_ERR_NOMEM; table->filled = 0; table->capacity = capacity; INIT_HLIST_HEAD(&table->extra_inodes); return 0; } /* Free the memory allocated by init_inode_table(). */ void destroy_inode_table(struct wim_inode_table *table) { FREE(table->array); } /* Double the capacity of the inode hash table. */ void enlarge_inode_table(struct wim_inode_table *table) { const size_t old_capacity = table->capacity; const size_t new_capacity = old_capacity * 2; struct hlist_head *old_array = table->array; struct hlist_head *new_array; struct wim_inode *inode; struct hlist_node *tmp; new_array = CALLOC(new_capacity, sizeof(struct hlist_head)); if (!new_array) return; table->array = new_array; table->capacity = new_capacity; for (size_t i = 0; i < old_capacity; i++) { hlist_for_each_entry_safe(inode, tmp, &old_array[i], i_hlist_node) { hlist_add_head(&inode->i_hlist_node, &new_array[hash_inode(table, inode->i_ino, inode->i_devno)]); } } FREE(old_array); } /* * Allocate a new dentry, with hard link detection. * * @table * The inode table being used for the current directory scan operation. It * will contain the mapping from (ino, devno) pairs to inodes. * * @name * The name to give the new dentry. * * @ino * The inode number of the file, read from the filesystem. * * @devno * The device number of the file, read from the filesystem. Proper setting * of this parameter prevents cross-device hardlinks from being created. * If this is not a problem (perhaps because the current directory scan * operation is guaranteed to never traverse a filesystem boundary), then * this parameter can just be a fixed value such as 0. * * @noshare * If %true, the new dentry will not be hard linked to any existing inode, * regardless of the values of @ino and @devno. If %false, normal hard * link detection will be done. * * @dentry_ret * On success, a pointer to the new dentry will be returned in this * location. If i_nlink of the dentry's inode is greater than 1, then this * function created a hard link to an existing inode rather than creating a * new inode. * * On success, returns 0. On failure, returns WIMLIB_ERR_NOMEM or an error code * resulting from a failed string conversion. */ int inode_table_new_dentry(struct wim_inode_table *table, const tchar *name, u64 ino, u64 devno, bool noshare, struct wim_dentry **dentry_ret) { struct wim_dentry *dentry; struct wim_inode *inode; struct hlist_head *list; int ret; if (noshare) { /* No hard link detection */ list = &table->extra_inodes; } else { /* Hard link detection */ list = &table->array[hash_inode(table, ino, devno)]; hlist_for_each_entry(inode, list, i_hlist_node) { if (inode->i_ino != ino || inode->i_devno != devno) continue; if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) { WARNING("Not honoring directory hard link " "of \"%"TS"\"", inode_any_full_path(inode)); continue; } /* Inode found; use it. */ return new_dentry_with_existing_inode(name, inode, dentry_ret); } /* Inode not found; create it. */ } ret = new_dentry_with_new_inode(name, false, &dentry); if (ret) return ret; inode = dentry->d_inode; inode->i_ino = ino; inode->i_devno = devno; hlist_add_head(&inode->i_hlist_node, list); if (list != &table->extra_inodes) if (++table->filled > table->capacity) enlarge_inode_table(table); *dentry_ret = dentry; return 0; } /* * Following the allocation of dentries with hard link detection using * inode_table_new_dentry(), this function will assign consecutive inode numbers * to the new set of inodes. It will also append the list of new inodes to the * list @head, which must contain any inodes already existing in the WIM image. */ void inode_table_prepare_inode_list(struct wim_inode_table *table, struct hlist_head *head) { struct wim_inode *inode; struct hlist_node *tmp; u64 cur_ino = 1; /* Re-assign inode numbers in the existing list to avoid duplicates. */ hlist_for_each_entry(inode, head, i_hlist_node) inode->i_ino = cur_ino++; /* Assign inode numbers to the new inodes and move them to the image's * inode list. */ for (size_t i = 0; i < table->capacity; i++) { hlist_for_each_entry_safe(inode, tmp, &table->array[i], i_hlist_node) { inode->i_ino = cur_ino++; hlist_add_head(&inode->i_hlist_node, head); } } hlist_for_each_entry_safe(inode, tmp, &table->extra_inodes, i_hlist_node) { inode->i_ino = cur_ino++; hlist_add_head(&inode->i_hlist_node, head); } } wimlib-1.13.1/src/lzms_common.c0000644000175000017500000006105613454471410013304 00000000000000/* * lzms_common.c - Common code for LZMS compression and decompression */ /* * Copyright (C) 2013-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib/lzms_common.h" #include "wimlib/unaligned.h" #include "wimlib/x86_cpu_features.h" #ifdef __x86_64__ # include #endif /* Table: offset slot => offset slot base value */ const u32 lzms_offset_slot_base[LZMS_MAX_NUM_OFFSET_SYMS + 1] = { 0x00000001, 0x00000002, 0x00000003, 0x00000004, 0x00000005, 0x00000006, 0x00000007, 0x00000008, 0x00000009, 0x0000000d, 0x00000011, 0x00000015, 0x00000019, 0x0000001d, 0x00000021, 0x00000025, 0x00000029, 0x0000002d, 0x00000035, 0x0000003d, 0x00000045, 0x0000004d, 0x00000055, 0x0000005d, 0x00000065, 0x00000075, 0x00000085, 0x00000095, 0x000000a5, 0x000000b5, 0x000000c5, 0x000000d5, 0x000000e5, 0x000000f5, 0x00000105, 0x00000125, 0x00000145, 0x00000165, 0x00000185, 0x000001a5, 0x000001c5, 0x000001e5, 0x00000205, 0x00000225, 0x00000245, 0x00000265, 0x00000285, 0x000002a5, 0x000002c5, 0x000002e5, 0x00000325, 0x00000365, 0x000003a5, 0x000003e5, 0x00000425, 0x00000465, 0x000004a5, 0x000004e5, 0x00000525, 0x00000565, 0x000005a5, 0x000005e5, 0x00000625, 0x00000665, 0x000006a5, 0x00000725, 0x000007a5, 0x00000825, 0x000008a5, 0x00000925, 0x000009a5, 0x00000a25, 0x00000aa5, 0x00000b25, 0x00000ba5, 0x00000c25, 0x00000ca5, 0x00000d25, 0x00000da5, 0x00000e25, 0x00000ea5, 0x00000f25, 0x00000fa5, 0x00001025, 0x000010a5, 0x000011a5, 0x000012a5, 0x000013a5, 0x000014a5, 0x000015a5, 0x000016a5, 0x000017a5, 0x000018a5, 0x000019a5, 0x00001aa5, 0x00001ba5, 0x00001ca5, 0x00001da5, 0x00001ea5, 0x00001fa5, 0x000020a5, 0x000021a5, 0x000022a5, 0x000023a5, 0x000024a5, 0x000026a5, 0x000028a5, 0x00002aa5, 0x00002ca5, 0x00002ea5, 0x000030a5, 0x000032a5, 0x000034a5, 0x000036a5, 0x000038a5, 0x00003aa5, 0x00003ca5, 0x00003ea5, 0x000040a5, 0x000042a5, 0x000044a5, 0x000046a5, 0x000048a5, 0x00004aa5, 0x00004ca5, 0x00004ea5, 0x000050a5, 0x000052a5, 0x000054a5, 0x000056a5, 0x000058a5, 0x00005aa5, 0x00005ca5, 0x00005ea5, 0x000060a5, 0x000064a5, 0x000068a5, 0x00006ca5, 0x000070a5, 0x000074a5, 0x000078a5, 0x00007ca5, 0x000080a5, 0x000084a5, 0x000088a5, 0x00008ca5, 0x000090a5, 0x000094a5, 0x000098a5, 0x00009ca5, 0x0000a0a5, 0x0000a4a5, 0x0000a8a5, 0x0000aca5, 0x0000b0a5, 0x0000b4a5, 0x0000b8a5, 0x0000bca5, 0x0000c0a5, 0x0000c4a5, 0x0000c8a5, 0x0000cca5, 0x0000d0a5, 0x0000d4a5, 0x0000d8a5, 0x0000dca5, 0x0000e0a5, 0x0000e4a5, 0x0000eca5, 0x0000f4a5, 0x0000fca5, 0x000104a5, 0x00010ca5, 0x000114a5, 0x00011ca5, 0x000124a5, 0x00012ca5, 0x000134a5, 0x00013ca5, 0x000144a5, 0x00014ca5, 0x000154a5, 0x00015ca5, 0x000164a5, 0x00016ca5, 0x000174a5, 0x00017ca5, 0x000184a5, 0x00018ca5, 0x000194a5, 0x00019ca5, 0x0001a4a5, 0x0001aca5, 0x0001b4a5, 0x0001bca5, 0x0001c4a5, 0x0001cca5, 0x0001d4a5, 0x0001dca5, 0x0001e4a5, 0x0001eca5, 0x0001f4a5, 0x0001fca5, 0x000204a5, 0x00020ca5, 0x000214a5, 0x00021ca5, 0x000224a5, 0x000234a5, 0x000244a5, 0x000254a5, 0x000264a5, 0x000274a5, 0x000284a5, 0x000294a5, 0x0002a4a5, 0x0002b4a5, 0x0002c4a5, 0x0002d4a5, 0x0002e4a5, 0x0002f4a5, 0x000304a5, 0x000314a5, 0x000324a5, 0x000334a5, 0x000344a5, 0x000354a5, 0x000364a5, 0x000374a5, 0x000384a5, 0x000394a5, 0x0003a4a5, 0x0003b4a5, 0x0003c4a5, 0x0003d4a5, 0x0003e4a5, 0x0003f4a5, 0x000404a5, 0x000414a5, 0x000424a5, 0x000434a5, 0x000444a5, 0x000454a5, 0x000464a5, 0x000474a5, 0x000484a5, 0x000494a5, 0x0004a4a5, 0x0004b4a5, 0x0004c4a5, 0x0004e4a5, 0x000504a5, 0x000524a5, 0x000544a5, 0x000564a5, 0x000584a5, 0x0005a4a5, 0x0005c4a5, 0x0005e4a5, 0x000604a5, 0x000624a5, 0x000644a5, 0x000664a5, 0x000684a5, 0x0006a4a5, 0x0006c4a5, 0x0006e4a5, 0x000704a5, 0x000724a5, 0x000744a5, 0x000764a5, 0x000784a5, 0x0007a4a5, 0x0007c4a5, 0x0007e4a5, 0x000804a5, 0x000824a5, 0x000844a5, 0x000864a5, 0x000884a5, 0x0008a4a5, 0x0008c4a5, 0x0008e4a5, 0x000904a5, 0x000924a5, 0x000944a5, 0x000964a5, 0x000984a5, 0x0009a4a5, 0x0009c4a5, 0x0009e4a5, 0x000a04a5, 0x000a24a5, 0x000a44a5, 0x000a64a5, 0x000aa4a5, 0x000ae4a5, 0x000b24a5, 0x000b64a5, 0x000ba4a5, 0x000be4a5, 0x000c24a5, 0x000c64a5, 0x000ca4a5, 0x000ce4a5, 0x000d24a5, 0x000d64a5, 0x000da4a5, 0x000de4a5, 0x000e24a5, 0x000e64a5, 0x000ea4a5, 0x000ee4a5, 0x000f24a5, 0x000f64a5, 0x000fa4a5, 0x000fe4a5, 0x001024a5, 0x001064a5, 0x0010a4a5, 0x0010e4a5, 0x001124a5, 0x001164a5, 0x0011a4a5, 0x0011e4a5, 0x001224a5, 0x001264a5, 0x0012a4a5, 0x0012e4a5, 0x001324a5, 0x001364a5, 0x0013a4a5, 0x0013e4a5, 0x001424a5, 0x001464a5, 0x0014a4a5, 0x0014e4a5, 0x001524a5, 0x001564a5, 0x0015a4a5, 0x0015e4a5, 0x001624a5, 0x001664a5, 0x0016a4a5, 0x0016e4a5, 0x001724a5, 0x001764a5, 0x0017a4a5, 0x0017e4a5, 0x001824a5, 0x001864a5, 0x0018a4a5, 0x0018e4a5, 0x001924a5, 0x001964a5, 0x0019e4a5, 0x001a64a5, 0x001ae4a5, 0x001b64a5, 0x001be4a5, 0x001c64a5, 0x001ce4a5, 0x001d64a5, 0x001de4a5, 0x001e64a5, 0x001ee4a5, 0x001f64a5, 0x001fe4a5, 0x002064a5, 0x0020e4a5, 0x002164a5, 0x0021e4a5, 0x002264a5, 0x0022e4a5, 0x002364a5, 0x0023e4a5, 0x002464a5, 0x0024e4a5, 0x002564a5, 0x0025e4a5, 0x002664a5, 0x0026e4a5, 0x002764a5, 0x0027e4a5, 0x002864a5, 0x0028e4a5, 0x002964a5, 0x0029e4a5, 0x002a64a5, 0x002ae4a5, 0x002b64a5, 0x002be4a5, 0x002c64a5, 0x002ce4a5, 0x002d64a5, 0x002de4a5, 0x002e64a5, 0x002ee4a5, 0x002f64a5, 0x002fe4a5, 0x003064a5, 0x0030e4a5, 0x003164a5, 0x0031e4a5, 0x003264a5, 0x0032e4a5, 0x003364a5, 0x0033e4a5, 0x003464a5, 0x0034e4a5, 0x003564a5, 0x0035e4a5, 0x003664a5, 0x0036e4a5, 0x003764a5, 0x0037e4a5, 0x003864a5, 0x0038e4a5, 0x003964a5, 0x0039e4a5, 0x003a64a5, 0x003ae4a5, 0x003b64a5, 0x003be4a5, 0x003c64a5, 0x003ce4a5, 0x003d64a5, 0x003de4a5, 0x003ee4a5, 0x003fe4a5, 0x0040e4a5, 0x0041e4a5, 0x0042e4a5, 0x0043e4a5, 0x0044e4a5, 0x0045e4a5, 0x0046e4a5, 0x0047e4a5, 0x0048e4a5, 0x0049e4a5, 0x004ae4a5, 0x004be4a5, 0x004ce4a5, 0x004de4a5, 0x004ee4a5, 0x004fe4a5, 0x0050e4a5, 0x0051e4a5, 0x0052e4a5, 0x0053e4a5, 0x0054e4a5, 0x0055e4a5, 0x0056e4a5, 0x0057e4a5, 0x0058e4a5, 0x0059e4a5, 0x005ae4a5, 0x005be4a5, 0x005ce4a5, 0x005de4a5, 0x005ee4a5, 0x005fe4a5, 0x0060e4a5, 0x0061e4a5, 0x0062e4a5, 0x0063e4a5, 0x0064e4a5, 0x0065e4a5, 0x0066e4a5, 0x0067e4a5, 0x0068e4a5, 0x0069e4a5, 0x006ae4a5, 0x006be4a5, 0x006ce4a5, 0x006de4a5, 0x006ee4a5, 0x006fe4a5, 0x0070e4a5, 0x0071e4a5, 0x0072e4a5, 0x0073e4a5, 0x0074e4a5, 0x0075e4a5, 0x0076e4a5, 0x0077e4a5, 0x0078e4a5, 0x0079e4a5, 0x007ae4a5, 0x007be4a5, 0x007ce4a5, 0x007de4a5, 0x007ee4a5, 0x007fe4a5, 0x0080e4a5, 0x0081e4a5, 0x0082e4a5, 0x0083e4a5, 0x0084e4a5, 0x0085e4a5, 0x0086e4a5, 0x0087e4a5, 0x0088e4a5, 0x0089e4a5, 0x008ae4a5, 0x008be4a5, 0x008ce4a5, 0x008de4a5, 0x008fe4a5, 0x0091e4a5, 0x0093e4a5, 0x0095e4a5, 0x0097e4a5, 0x0099e4a5, 0x009be4a5, 0x009de4a5, 0x009fe4a5, 0x00a1e4a5, 0x00a3e4a5, 0x00a5e4a5, 0x00a7e4a5, 0x00a9e4a5, 0x00abe4a5, 0x00ade4a5, 0x00afe4a5, 0x00b1e4a5, 0x00b3e4a5, 0x00b5e4a5, 0x00b7e4a5, 0x00b9e4a5, 0x00bbe4a5, 0x00bde4a5, 0x00bfe4a5, 0x00c1e4a5, 0x00c3e4a5, 0x00c5e4a5, 0x00c7e4a5, 0x00c9e4a5, 0x00cbe4a5, 0x00cde4a5, 0x00cfe4a5, 0x00d1e4a5, 0x00d3e4a5, 0x00d5e4a5, 0x00d7e4a5, 0x00d9e4a5, 0x00dbe4a5, 0x00dde4a5, 0x00dfe4a5, 0x00e1e4a5, 0x00e3e4a5, 0x00e5e4a5, 0x00e7e4a5, 0x00e9e4a5, 0x00ebe4a5, 0x00ede4a5, 0x00efe4a5, 0x00f1e4a5, 0x00f3e4a5, 0x00f5e4a5, 0x00f7e4a5, 0x00f9e4a5, 0x00fbe4a5, 0x00fde4a5, 0x00ffe4a5, 0x0101e4a5, 0x0103e4a5, 0x0105e4a5, 0x0107e4a5, 0x0109e4a5, 0x010be4a5, 0x010de4a5, 0x010fe4a5, 0x0111e4a5, 0x0113e4a5, 0x0115e4a5, 0x0117e4a5, 0x0119e4a5, 0x011be4a5, 0x011de4a5, 0x011fe4a5, 0x0121e4a5, 0x0123e4a5, 0x0125e4a5, 0x0127e4a5, 0x0129e4a5, 0x012be4a5, 0x012de4a5, 0x012fe4a5, 0x0131e4a5, 0x0133e4a5, 0x0135e4a5, 0x0137e4a5, 0x013be4a5, 0x013fe4a5, 0x0143e4a5, 0x0147e4a5, 0x014be4a5, 0x014fe4a5, 0x0153e4a5, 0x0157e4a5, 0x015be4a5, 0x015fe4a5, 0x0163e4a5, 0x0167e4a5, 0x016be4a5, 0x016fe4a5, 0x0173e4a5, 0x0177e4a5, 0x017be4a5, 0x017fe4a5, 0x0183e4a5, 0x0187e4a5, 0x018be4a5, 0x018fe4a5, 0x0193e4a5, 0x0197e4a5, 0x019be4a5, 0x019fe4a5, 0x01a3e4a5, 0x01a7e4a5, 0x01abe4a5, 0x01afe4a5, 0x01b3e4a5, 0x01b7e4a5, 0x01bbe4a5, 0x01bfe4a5, 0x01c3e4a5, 0x01c7e4a5, 0x01cbe4a5, 0x01cfe4a5, 0x01d3e4a5, 0x01d7e4a5, 0x01dbe4a5, 0x01dfe4a5, 0x01e3e4a5, 0x01e7e4a5, 0x01ebe4a5, 0x01efe4a5, 0x01f3e4a5, 0x01f7e4a5, 0x01fbe4a5, 0x01ffe4a5, 0x0203e4a5, 0x0207e4a5, 0x020be4a5, 0x020fe4a5, 0x0213e4a5, 0x0217e4a5, 0x021be4a5, 0x021fe4a5, 0x0223e4a5, 0x0227e4a5, 0x022be4a5, 0x022fe4a5, 0x0233e4a5, 0x0237e4a5, 0x023be4a5, 0x023fe4a5, 0x0243e4a5, 0x0247e4a5, 0x024be4a5, 0x024fe4a5, 0x0253e4a5, 0x0257e4a5, 0x025be4a5, 0x025fe4a5, 0x0263e4a5, 0x0267e4a5, 0x026be4a5, 0x026fe4a5, 0x0273e4a5, 0x0277e4a5, 0x027be4a5, 0x027fe4a5, 0x0283e4a5, 0x0287e4a5, 0x028be4a5, 0x028fe4a5, 0x0293e4a5, 0x0297e4a5, 0x029be4a5, 0x029fe4a5, 0x02a3e4a5, 0x02a7e4a5, 0x02abe4a5, 0x02afe4a5, 0x02b3e4a5, 0x02bbe4a5, 0x02c3e4a5, 0x02cbe4a5, 0x02d3e4a5, 0x02dbe4a5, 0x02e3e4a5, 0x02ebe4a5, 0x02f3e4a5, 0x02fbe4a5, 0x0303e4a5, 0x030be4a5, 0x0313e4a5, 0x031be4a5, 0x0323e4a5, 0x032be4a5, 0x0333e4a5, 0x033be4a5, 0x0343e4a5, 0x034be4a5, 0x0353e4a5, 0x035be4a5, 0x0363e4a5, 0x036be4a5, 0x0373e4a5, 0x037be4a5, 0x0383e4a5, 0x038be4a5, 0x0393e4a5, 0x039be4a5, 0x03a3e4a5, 0x03abe4a5, 0x03b3e4a5, 0x03bbe4a5, 0x03c3e4a5, 0x03cbe4a5, 0x03d3e4a5, 0x03dbe4a5, 0x03e3e4a5, 0x03ebe4a5, 0x03f3e4a5, 0x03fbe4a5, 0x0403e4a5, 0x040be4a5, 0x0413e4a5, 0x041be4a5, 0x0423e4a5, 0x042be4a5, 0x0433e4a5, 0x043be4a5, 0x0443e4a5, 0x044be4a5, 0x0453e4a5, 0x045be4a5, 0x0463e4a5, 0x046be4a5, 0x0473e4a5, 0x047be4a5, 0x0483e4a5, 0x048be4a5, 0x0493e4a5, 0x049be4a5, 0x04a3e4a5, 0x04abe4a5, 0x04b3e4a5, 0x04bbe4a5, 0x04c3e4a5, 0x04cbe4a5, 0x04d3e4a5, 0x04dbe4a5, 0x04e3e4a5, 0x04ebe4a5, 0x04f3e4a5, 0x04fbe4a5, 0x0503e4a5, 0x050be4a5, 0x0513e4a5, 0x051be4a5, 0x0523e4a5, 0x052be4a5, 0x0533e4a5, 0x053be4a5, 0x0543e4a5, 0x054be4a5, 0x0553e4a5, 0x055be4a5, 0x0563e4a5, 0x056be4a5, 0x0573e4a5, 0x057be4a5, 0x0583e4a5, 0x058be4a5, 0x0593e4a5, 0x059be4a5, 0x05a3e4a5, 0x05abe4a5, 0x05b3e4a5, 0x05bbe4a5, 0x05c3e4a5, 0x05cbe4a5, 0x05d3e4a5, 0x05dbe4a5, 0x05e3e4a5, 0x05ebe4a5, 0x05f3e4a5, 0x05fbe4a5, 0x060be4a5, 0x061be4a5, 0x062be4a5, 0x063be4a5, 0x064be4a5, 0x065be4a5, 0x465be4a5, /* The last entry is extra; it is equal to LZMS_MAX_MATCH_OFFSET + 1 and * is here to aid binary search. */ }; /* Table: offset slot => number of extra offset bits */ const u8 lzms_extra_offset_bits[LZMS_MAX_NUM_OFFSET_SYMS] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2, 2 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4, 4 , 4 , 5 , 5 , 5 , 5 , 5 , 5 , 5 , 5 , 5 , 5 , 5 , 5 , 5 , 5, 5 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6, 7 , 7 , 7 , 7 , 7 , 7 , 7 , 7 , 7 , 7 , 7 , 7 , 7 , 7 , 7 , 7, 7 , 7 , 7 , 7 , 8 , 8 , 8 , 8 , 8 , 8 , 8 , 8 , 8 , 8 , 8 , 8, 8 , 8 , 8 , 8 , 8 , 8 , 8 , 8 , 9 , 9 , 9 , 9 , 9 , 9 , 9 , 9, 9 , 9 , 9 , 9 , 9 , 9 , 9 , 9 , 9 , 9 , 9 , 9 , 9 , 9 , 9 , 9, 9 , 9 , 9 , 9 , 9 , 9 , 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 30, }; /* Table: length slot => length slot base value */ const u32 lzms_length_slot_base[LZMS_NUM_LENGTH_SYMS + 1] = { 0x00000001, 0x00000002, 0x00000003, 0x00000004, 0x00000005, 0x00000006, 0x00000007, 0x00000008, 0x00000009, 0x0000000a, 0x0000000b, 0x0000000c, 0x0000000d, 0x0000000e, 0x0000000f, 0x00000010, 0x00000011, 0x00000012, 0x00000013, 0x00000014, 0x00000015, 0x00000016, 0x00000017, 0x00000018, 0x00000019, 0x0000001a, 0x0000001b, 0x0000001d, 0x0000001f, 0x00000021, 0x00000023, 0x00000027, 0x0000002b, 0x0000002f, 0x00000033, 0x00000037, 0x0000003b, 0x00000043, 0x0000004b, 0x00000053, 0x0000005b, 0x0000006b, 0x0000007b, 0x0000008b, 0x0000009b, 0x000000ab, 0x000000cb, 0x000000eb, 0x0000012b, 0x000001ab, 0x000002ab, 0x000004ab, 0x000008ab, 0x000108ab, 0x400108ab, /* The last entry is extra; it is equal to LZMS_MAX_MATCH_LENGTH + 1 and * is here to aid binary search. */ }; /* Table: length slot => number of extra length bits */ const u8 lzms_extra_length_bits[LZMS_NUM_LENGTH_SYMS] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 2 , 2 , 2 , 2 , 2 , 2 , 3 , 3 , 3 , 3 , 4 , 4 , 4 , 4 , 4 , 5 , 5 , 6 , 7 , 8 , 9 , 10, 16, 30, }; unsigned lzms_get_slot(u32 value, const u32 slot_base_tab[], unsigned num_slots) { unsigned l = 0; unsigned r = num_slots - 1; for (;;) { unsigned slot = (l + r) / 2; if (value >= slot_base_tab[slot]) { if (value < slot_base_tab[slot + 1]) return slot; else l = slot + 1; } else { r = slot - 1; } } } /* Return the number of offset slots used when processing a buffer having the * specified uncompressed size. */ unsigned lzms_get_num_offset_slots(size_t uncompressed_size) { if (uncompressed_size < 2) return 0; return 1 + lzms_get_offset_slot(uncompressed_size - 1); } void lzms_init_probabilities(struct lzms_probabilites *probs) { struct lzms_probability_entry *entries = (struct lzms_probability_entry *)probs; size_t num_entries = sizeof(struct lzms_probabilites) / sizeof(struct lzms_probability_entry); for (size_t i = 0; i < num_entries; i++) { entries[i].num_recent_zero_bits = LZMS_INITIAL_PROBABILITY; entries[i].recent_bits = LZMS_INITIAL_RECENT_BITS; } } void lzms_init_symbol_frequencies(u32 freqs[], unsigned num_syms) { for (unsigned sym = 0; sym < num_syms; sym++) freqs[sym] = 1; } void lzms_dilute_symbol_frequencies(u32 freqs[], unsigned num_syms) { for (unsigned sym = 0; sym < num_syms; sym++) freqs[sym] = (freqs[sym] >> 1) + 1; } #ifdef __x86_64__ static forceinline u8 * find_next_opcode_sse4_2(u8 *p) { const __v16qi potential_opcodes = (__v16qi) {0x48, 0x4C, 0xE8, 0xE9, 0xF0, 0xFF}; __asm__( " pcmpestri $0x0, (%[p]), %[potential_opcodes] \n" " jc 2f \n" "1: \n" " add $0x10, %[p] \n" " pcmpestri $0x0, (%[p]), %[potential_opcodes] \n" " jnc 1b \n" "2: \n" #ifdef __ILP32__ /* x32 ABI (x86_64 with 32-bit pointers) */ " add %%ecx, %[p] \n" #else " add %%rcx, %[p] \n" #endif : [p] "+r" (p) : [potential_opcodes] "x" (potential_opcodes), "a" (6), "d" (16) : "rcx", "cc" ); return p; } #endif /* __x86_64__ */ static forceinline u8 * find_next_opcode_default(u8 *p) { /* * The following table is used to accelerate the common case where the * byte has nothing to do with x86 translation and must simply be * skipped. This was faster than the following alternatives: * - Jump table with 256 entries * - Switch statement with default */ static const u8 is_potential_opcode[256] = { [0x48] = 1, [0x4C] = 1, [0xE8] = 1, [0xE9] = 1, [0xF0] = 1, [0xFF] = 1, }; for (;;) { if (is_potential_opcode[*p]) break; p++; if (is_potential_opcode[*p]) break; p++; if (is_potential_opcode[*p]) break; p++; if (is_potential_opcode[*p]) break; p++; } return p; } static forceinline u8 * translate_if_needed(u8 *data, u8 *p, s32 *last_x86_pos, s32 last_target_usages[], bool undo) { s32 max_trans_offset; s32 opcode_nbytes; u16 target16; s32 i; max_trans_offset = LZMS_X86_MAX_TRANSLATION_OFFSET; /* * p[0] has one of the following values: * 0x48 0x4C 0xE8 0xE9 0xF0 0xFF */ if (p[0] >= 0xF0) { if (p[0] & 0x0F) { /* 0xFF (instruction group) */ if (p[1] == 0x15) { /* Call indirect relative */ opcode_nbytes = 2; goto have_opcode; } } else { /* 0xF0 (lock prefix) */ if (p[1] == 0x83 && p[2] == 0x05) { /* Lock add relative */ opcode_nbytes = 3; goto have_opcode; } } } else if (p[0] <= 0x4C) { /* 0x48 or 0x4C. In 64-bit code this is a REX prefix byte with * W=1, R=[01], X=0, and B=0, and it will be followed by the * actual opcode, then additional bytes depending on the opcode. * We are most interested in several common instructions that * access data relative to the instruction pointer. These use a * 1-byte opcode, followed by a ModR/M byte, followed by a * 4-byte displacement. */ /* Test: does the ModR/M byte indicate RIP-relative addressing? * Note: there seems to be a mistake in the format here; the * mask really should be 0xC7 instead of 0x07 so that both the * MOD and R/M fields of ModR/M are tested, not just R/M. */ if ((p[2] & 0x07) == 0x05) { /* Check for the LEA (load effective address) or MOV * (move) opcodes. For MOV there are additional * restrictions, although it seems they are only helpful * due to the overly lax ModR/M test. */ if (p[1] == 0x8D || (p[1] == 0x8B && !(p[0] & 0x04) && !(p[2] & 0xF0))) { opcode_nbytes = 3; goto have_opcode; } } } else { if (p[0] & 0x01) { /* 0xE9: Jump relative. Theoretically this would be * useful to translate, but in fact it's explicitly * excluded. Most likely it creates too many false * positives for the detection algorithm. */ p += 4; } else { /* 0xE8: Call relative. This is a common case, so it * uses a reduced max_trans_offset. In other words, we * have to be more confident that the data actually is * x86 machine code before we'll do the translation. */ opcode_nbytes = 1; max_trans_offset >>= 1; goto have_opcode; } } return p + 1; have_opcode: i = p - data; p += opcode_nbytes; if (undo) { if (i - *last_x86_pos <= max_trans_offset) { u32 n = get_unaligned_le32(p); put_unaligned_le32(n - i, p); } target16 = i + get_unaligned_le16(p); } else { target16 = i + get_unaligned_le16(p); if (i - *last_x86_pos <= max_trans_offset) { u32 n = get_unaligned_le32(p); put_unaligned_le32(n + i, p); } } i += opcode_nbytes + sizeof(le32) - 1; if (i - last_target_usages[target16] <= LZMS_X86_ID_WINDOW_SIZE) *last_x86_pos = i; last_target_usages[target16] = i; return p + sizeof(le32); } /* * Translate relative addresses embedded in x86 instructions into absolute * addresses (@undo == %false), or undo this translation (@undo == %true). * * Absolute addresses are usually more compressible by LZ factorization. * * @last_target_usages must be a temporary array of length >= 65536. */ void lzms_x86_filter(u8 data[restrict], s32 size, s32 last_target_usages[restrict], bool undo) { /* * Note: this filter runs unconditionally and uses a custom algorithm to * detect data regions that probably contain x86 code. * * 'last_x86_pos' tracks the most recent position that has a good chance * of being the start of an x86 instruction. When the filter detects a * likely x86 instruction, it updates this variable and considers the * next LZMS_X86_MAX_TRANSLATION_OFFSET bytes of data as valid for x86 * translations. * * If part of the data does not, in fact, contain x86 machine code, then * 'last_x86_pos' will, very likely, eventually fall more than * LZMS_X86_MAX_TRANSLATION_OFFSET bytes behind the current position. * This results in x86 translations being disabled until the next likely * x86 instruction is detected. * * To identify "likely x86 instructions", the algorithm attempts to * track the position of the most recent potential relative-addressing * instruction that referenced each possible memory address. If it * finds two references to the same memory address within an * LZMS_X86_ID_WINDOW_SIZE-byte sized window, then the second reference * is flagged as a likely x86 instruction. Since the instructions * considered for translation necessarily use relative addressing, the * algorithm does a tentative translation into absolute addresses. In * addition, so that memory addresses can be looked up in an array of * reasonable size (in this code, 'last_target_usages'), only the * low-order 2 bytes of each address are considered significant. */ u8 *p; u8 *tail_ptr; s32 last_x86_pos = -LZMS_X86_MAX_TRANSLATION_OFFSET - 1; if (size <= 17) return; for (s32 i = 0; i < 65536; i++) last_target_usages[i] = -(s32)LZMS_X86_ID_WINDOW_SIZE - 1; /* * Optimization: only check for end-of-buffer when we already have a * byte that is a potential opcode for x86 translation. To do this, * overwrite one of the bytes near the end of the buffer, and restore it * later. The correctness of this optimization relies on two * characteristics of compressed format: * * 1. No translation can follow an opcode beginning in the last 16 * bytes. * 2. A translation following an opcode starting at the last possible * position (17 bytes from the end) never extends more than 7 bytes. * Consequently, we can overwrite any of the bytes starting at * data[(size - 16) + 7] and have no effect on the result, as long * as we restore those bytes later. */ /* Note: the very first byte must be ignored completely! */ p = data + 1; tail_ptr = &data[size - 16]; #ifdef __x86_64__ if (x86_have_cpu_feature(X86_CPU_FEATURE_SSE4_2)) { u8 saved_byte = *tail_ptr; *tail_ptr = 0xE8; for (;;) { u8 *new_p = find_next_opcode_sse4_2(p); if (new_p >= tail_ptr - 8) break; p = new_p; p = translate_if_needed(data, p, &last_x86_pos, last_target_usages, undo); } *tail_ptr = saved_byte; } #endif { u8 saved_byte = *(tail_ptr + 8); *(tail_ptr + 8) = 0xE8; for (;;) { p = find_next_opcode_default(p); if (p >= tail_ptr) break; p = translate_if_needed(data, p, &last_x86_pos, last_target_usages, undo); } *(tail_ptr + 8) = saved_byte; } } wimlib-1.13.1/src/join.c0000644000175000017500000000744213160354224011702 00000000000000/* * join.c * * Join split WIMs (sometimes named as .swm files) together into one WIM. */ /* * Copyright (C) 2012-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include "wimlib.h" #include "wimlib/error.h" #include "wimlib/types.h" #include "wimlib/util.h" #include "wimlib/wim.h" /* * Verify that a list of WIM files sorted by part number is a spanned set. * * Return: 0 on success; WIMLIB_ERR_SPLIT_INVALID if the set is not valid. */ static int verify_swm_set(WIMStruct * const *swms, unsigned num_swms) { for (unsigned i = 0; i < num_swms; i++) { if (!guids_equal(swms[i]->hdr.guid, swms[0]->hdr.guid)) { ERROR("The split WIM parts specified belong to " "different split WIMs!"); return WIMLIB_ERR_SPLIT_INVALID; } if (swms[i]->hdr.total_parts != num_swms) { ERROR("\"%"TS"\" says there are %u parts in the split " "WIM, but %s%u part%s provided", swms[i]->filename, swms[i]->hdr.total_parts, num_swms < swms[i]->hdr.total_parts ? "only ":"", num_swms, num_swms > 1 ? "s were" : " was"); return WIMLIB_ERR_SPLIT_INVALID; } if (swms[i]->hdr.part_number != i + 1) { ERROR("The parts of the split WIM are not numbered " "1..%u as expected. Did you specify duplicate " "parts?", num_swms); return WIMLIB_ERR_SPLIT_INVALID; } } return 0; } static int cmp_swms_by_part_number(const void *p1, const void *p2) { WIMStruct *swm1 = *(WIMStruct **)p1; WIMStruct *swm2 = *(WIMStruct **)p2; return (int)swm1->hdr.part_number - (int)swm2->hdr.part_number; } WIMLIBAPI int wimlib_join_with_progress(const tchar * const *swm_names, unsigned num_swms, const tchar *output_path, int swm_open_flags, int wim_write_flags, wimlib_progress_func_t progfunc, void *progctx) { WIMStruct **swms; unsigned i; int ret; if (num_swms < 1 || num_swms > 0xffff) return WIMLIB_ERR_INVALID_PARAM; swms = CALLOC(num_swms, sizeof(swms[0])); if (!swms) return WIMLIB_ERR_NOMEM; for (i = 0; i < num_swms; i++) { ret = wimlib_open_wim_with_progress(swm_names[i], swm_open_flags, &swms[i], progfunc, progctx); if (ret) goto out; } qsort(swms, num_swms, sizeof(swms[0]), cmp_swms_by_part_number); ret = verify_swm_set(swms, num_swms); if (ret) goto out; ret = wimlib_reference_resources(swms[0], swms + 1, num_swms - 1, 0); if (ret) goto out; /* It is reasonably safe to provide WIMLIB_WRITE_FLAG_STREAMS_OK, as we * have verified that the specified split WIM parts form a spanned set. */ ret = wimlib_write(swms[0], output_path, WIMLIB_ALL_IMAGES, wim_write_flags | WIMLIB_WRITE_FLAG_STREAMS_OK | WIMLIB_WRITE_FLAG_RETAIN_GUID, 1); out: for (i = 0; i < num_swms; i++) wimlib_free(swms[i]); FREE(swms); return ret; } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_join(const tchar * const *swm_names, unsigned num_swms, const tchar *output_path, int swm_open_flags, int wim_write_flags) { return wimlib_join_with_progress(swm_names, num_swms, output_path, swm_open_flags, wim_write_flags, NULL, NULL); } wimlib-1.13.1/src/reparse.c0000644000175000017500000003631513160354225012406 00000000000000/* * reparse.c - Reparse point handling */ /* * Copyright (C) 2012, 2013, 2015 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include "wimlib/alloca.h" #include "wimlib/blob_table.h" #include "wimlib/endianness.h" #include "wimlib/encoding.h" #include "wimlib/error.h" #include "wimlib/guid.h" #include "wimlib/inode.h" #include "wimlib/reparse.h" #include "wimlib/resource.h" /* * Reconstruct the header of a reparse point buffer. This is necessary because * only reparse data is stored in WIM files. The reparse tag is instead stored * in the on-disk WIM dentry, and the reparse data length is equal to the size * of the blob in which the reparse data was stored, minus the size of a GUID * (16 bytes) if the reparse tag does not have the "Microsoft" bit set. */ void complete_reparse_point(struct reparse_buffer_disk *rpbuf, const struct wim_inode *inode, u16 blob_size) { rpbuf->rptag = cpu_to_le32(inode->i_reparse_tag); if (blob_size >= GUID_SIZE && !(inode->i_reparse_tag & 0x80000000)) blob_size -= GUID_SIZE; rpbuf->rpdatalen = cpu_to_le16(blob_size); rpbuf->rpreserved = cpu_to_le16(inode->i_rp_reserved); } /* Parse the buffer for a symbolic link or junction reparse point and fill in a * 'struct link_reparse_point'. */ int parse_link_reparse_point(const struct reparse_buffer_disk *rpbuf, u16 rpbuflen, struct link_reparse_point *link) { u16 substitute_name_offset; u16 print_name_offset; const u8 *data; link->rptag = le32_to_cpu(rpbuf->rptag); /* Not a symbolic link or junction? */ if (link->rptag != WIM_IO_REPARSE_TAG_SYMLINK && link->rptag != WIM_IO_REPARSE_TAG_MOUNT_POINT) return WIMLIB_ERR_INVALID_REPARSE_DATA; /* Is the buffer too small to be a symlink or a junction? */ if (rpbuflen < offsetof(struct reparse_buffer_disk, link.junction.data)) return WIMLIB_ERR_INVALID_REPARSE_DATA; link->rpreserved = le16_to_cpu(rpbuf->rpreserved); link->substitute_name_nbytes = le16_to_cpu(rpbuf->link.substitute_name_nbytes); substitute_name_offset = le16_to_cpu(rpbuf->link.substitute_name_offset); link->print_name_nbytes = le16_to_cpu(rpbuf->link.print_name_nbytes); print_name_offset = le16_to_cpu(rpbuf->link.print_name_offset); /* The names must be properly sized and aligned. */ if ((substitute_name_offset | print_name_offset | link->substitute_name_nbytes | link->print_name_nbytes) & 1) return WIMLIB_ERR_INVALID_REPARSE_DATA; if (link->rptag == WIM_IO_REPARSE_TAG_SYMLINK) { if (rpbuflen < offsetof(struct reparse_buffer_disk, link.symlink.data)) return WIMLIB_ERR_INVALID_REPARSE_DATA; link->symlink_flags = le32_to_cpu(rpbuf->link.symlink.flags); data = rpbuf->link.symlink.data; } else { data = rpbuf->link.junction.data; } /* Verify that the names don't overflow the buffer. */ if ((data - (const u8 *)rpbuf) + substitute_name_offset + link->substitute_name_nbytes > rpbuflen) return WIMLIB_ERR_INVALID_REPARSE_DATA; if ((data - (const u8 *)rpbuf) + print_name_offset + link->print_name_nbytes > rpbuflen) return WIMLIB_ERR_INVALID_REPARSE_DATA; /* Save the name pointers. */ link->substitute_name = (utf16lechar *)&data[substitute_name_offset]; link->print_name = (utf16lechar *)&data[print_name_offset]; return 0; } /* Translate a 'struct link_reparse_point' into a reparse point buffer. */ int make_link_reparse_point(const struct link_reparse_point *link, struct reparse_buffer_disk *rpbuf, u16 *rpbuflen_ret) { u8 *data; if (link->rptag == WIM_IO_REPARSE_TAG_SYMLINK) data = rpbuf->link.symlink.data; else if (link->rptag == WIM_IO_REPARSE_TAG_MOUNT_POINT) data = rpbuf->link.junction.data; else /* Callers should forbid this case, but check anyway. */ return WIMLIB_ERR_INVALID_REPARSE_DATA; /* Check if the names are too long to fit in a reparse point. */ if ((data - (u8 *)rpbuf) + link->substitute_name_nbytes + link->print_name_nbytes + 2 * sizeof(utf16lechar) > REPARSE_POINT_MAX_SIZE) return WIMLIB_ERR_INVALID_REPARSE_DATA; rpbuf->rptag = cpu_to_le32(link->rptag); rpbuf->rpreserved = cpu_to_le16(link->rpreserved); rpbuf->link.substitute_name_offset = cpu_to_le16(0); rpbuf->link.substitute_name_nbytes = cpu_to_le16(link->substitute_name_nbytes); rpbuf->link.print_name_offset = cpu_to_le16(link->substitute_name_nbytes + sizeof(utf16lechar)); rpbuf->link.print_name_nbytes = cpu_to_le16(link->print_name_nbytes); if (link->rptag == WIM_IO_REPARSE_TAG_SYMLINK) rpbuf->link.symlink.flags = cpu_to_le32(link->symlink_flags); /* We null-terminate the substitute and print names, although this isn't * strictly necessary. Note that the nbytes fields do not include the * null terminators. */ data = mempcpy(data, link->substitute_name, link->substitute_name_nbytes); *(utf16lechar *)data = cpu_to_le16(0); data += sizeof(utf16lechar); data = mempcpy(data, link->print_name, link->print_name_nbytes); *(utf16lechar *)data = cpu_to_le16(0); data += sizeof(utf16lechar); rpbuf->rpdatalen = cpu_to_le16(data - rpbuf->rpdata); *rpbuflen_ret = data - (u8 *)rpbuf; return 0; } /* UNIX symlink <=> Windows reparse point translation */ #ifndef __WIN32__ /* Retrieve the inode's reparse point buffer into @rpbuf and @rpbuflen_ret. * This gets the reparse data from @blob if specified, otherwise from the * inode's reparse point stream. The inode's streams must be resolved. */ static int wim_inode_get_reparse_point(const struct wim_inode *inode, struct reparse_buffer_disk *rpbuf, u16 *rpbuflen_ret, const struct blob_descriptor *blob) { int ret; u16 blob_size = 0; if (!blob) { const struct wim_inode_stream *strm; strm = inode_get_unnamed_stream(inode, STREAM_TYPE_REPARSE_POINT); if (strm) blob = stream_blob_resolved(strm); } if (blob) { if (blob->size > REPARSE_DATA_MAX_SIZE) return WIMLIB_ERR_INVALID_REPARSE_DATA; blob_size = blob->size; ret = read_blob_into_buf(blob, rpbuf->rpdata); if (ret) return ret; } complete_reparse_point(rpbuf, inode, blob_size); *rpbuflen_ret = REPARSE_DATA_OFFSET + blob_size; return 0; } static void copy(char **buf_p, size_t *bufsize_p, const char *src, size_t src_size) { size_t n = min(*bufsize_p, src_size); memcpy(*buf_p, src, n); *buf_p += n; *bufsize_p -= n; } /* * Get a UNIX-style symlink target from the WIM inode for a reparse point. * * @inode * The inode from which to read the symlink. If not a symbolic link or * junction reparse point, then -EINVAL will be returned. * @buf * Buffer into which to place the link target. * @bufsize * Available space in @buf, in bytes. * @blob * If not NULL, the blob from which to read the reparse data. Otherwise, * the reparse data will be read from the reparse point stream of @inode. * @altroot * If @altroot_len != 0 and the link is an absolute link that was stored as * "fixed", then prepend this path to the link target. * @altroot_len * Length of the @altroot string or 0. * * Similar to POSIX readlink(), this function writes as much of the symlink * target as possible (up to @bufsize bytes) to @buf with no null terminator and * returns the number of bytes written or a negative errno value on error. Note * that the target is truncated and @bufsize is returned in the overflow case. */ int wim_inode_readlink(const struct wim_inode *inode, char *buf, size_t bufsize, const struct blob_descriptor *blob, const char *altroot, size_t altroot_len) { struct reparse_buffer_disk rpbuf; u16 rpbuflen; struct link_reparse_point link; char *target_buffer; char *target; size_t target_len; char *buf_ptr; bool rpfix_ok = false; /* Not a symbolic link or junction? */ if (!inode_is_symlink(inode)) return -EINVAL; /* Retrieve the native Windows "substitute name". */ if (wim_inode_get_reparse_point(inode, &rpbuf, &rpbuflen, blob)) return -EIO; if (parse_link_reparse_point(&rpbuf, rpbuflen, &link)) return -EINVAL; /* Translate the substitute name to a multibyte string. */ if (utf16le_to_tstr(link.substitute_name, link.substitute_name_nbytes, &target_buffer, &target_len)) return -errno; target = target_buffer; /* * The substitute name is a native Windows NT path. There are two cases: * * 1. The reparse point is a symlink (rptag=WIM_IO_REPARSE_TAG_SYMLINK) * and SYMBOLIC_LINK_RELATIVE is set. Windows resolves the path * relative to the directory containing the reparse point file. In * this case, we just translate the path separators. * 2. Otherwise, Windows resolves the path from the root of the Windows * NT kernel object namespace. In this case, we attempt to strip the * device name, in addition to translating the path separators; e.g. * "\??\C:\Users\Public" is translated to "/Users/Public". * * Also in case (2) the link target may have been stored as "fixed", * meaning that with the device portion stripped off it is effectively * "relative to the root of the WIM image". If this is the case, and if * the caller provided an alternate root directory, then rewrite the * link to be relative to that directory. */ if (!link_is_relative_symlink(&link)) { static const char *const nt_root_dirs[] = { "\\??\\", "\\DosDevices\\", "\\Device\\", }; for (size_t i = 0; i < ARRAY_LEN(nt_root_dirs); i++) { size_t len = strlen(nt_root_dirs[i]); if (!strncmp(target, nt_root_dirs[i], len)) { char *p = target + len; while (*p == '\\') p++; while (*p && *p != '\\') p++; target_len -= (p - target); target = p; break; } } if (!(inode->i_rp_flags & WIM_RP_FLAG_NOT_FIXED)) rpfix_ok = true; } /* Translate backslashes (Windows NT path separator) to forward slashes * (UNIX path separator). In addition, translate forwards slashes to * backslashes; this enables lossless handling of UNIX symbolic link * targets that contain the backslash character. */ for (char *p = target; *p; p++) { if (*p == '\\') *p = '/'; else if (*p == '/') *p = '\\'; } /* Copy as much of the link target as possible to the output buffer and * return the number of bytes copied. */ buf_ptr = buf; if (rpfix_ok && altroot_len != 0) { copy(&buf_ptr, &bufsize, altroot, altroot_len); } else if (target_len == 0) { /* An absolute link target that was made relative to the same * directory pointed to will end up empty if the original target * did not have a trailing slash. Here, we are reading this * adjusted link target without prefixing it. This usually * doesn't happen, but if it does then we need to change it to * "/" so that it is a valid target. */ target = "/"; target_len = 1; } copy(&buf_ptr, &bufsize, target, target_len); FREE(target_buffer); return buf_ptr - buf; } /* Given a UNIX-style symbolic link target, create a Windows-style reparse point * buffer and assign it to the specified inode. */ int wim_inode_set_symlink(struct wim_inode *inode, const char *_target, struct blob_table *blob_table) { int ret; utf16lechar *target; size_t target_nbytes; struct link_reparse_point link; struct reparse_buffer_disk rpbuf; u16 rpbuflen; /* Translate the link target to UTF-16LE. */ ret = tstr_to_utf16le(_target, strlen(_target), &target, &target_nbytes); if (ret) return ret; /* Translate forward slashes (UNIX path separator) to backslashes * (Windows NT path separator). In addition, translate backslashes to * forward slashes; this enables lossless handling of UNIX symbolic link * targets that contain the backslash character. */ for (utf16lechar *p = target; *p; p++) { if (*p == cpu_to_le16('/')) *p = cpu_to_le16('\\'); else if (*p == cpu_to_le16('\\')) *p = cpu_to_le16('/'); } link.rptag = WIM_IO_REPARSE_TAG_SYMLINK; link.rpreserved = 0; /* Note: an absolute link that was rewritten to be relative to another * directory is assumed to either be empty or to have a leading slash. * See unix_relativize_link_target(). */ if (*target == cpu_to_le16('\\') || !*target) { /* * UNIX link target was absolute. In this case we represent the * link as a symlink reparse point with SYMBOLIC_LINK_RELATIVE * cleared. For this to work we need to assign it a path that * can be resolved from the root of the Windows NT kernel object * namespace. We do this by using "\??\C:" as a dummy prefix. * * Note that we could instead represent UNIX absolute links by * setting SYMBOLIC_LINK_RELATIVE and then leaving the path * backslash-prefixed like "\Users\Public". On Windows this is * valid and denotes a path relative to the root of the * filesystem on which the reparse point resides. The problem * with this is that neither WIMGAPI nor wimlib (on Windows) * will do "reparse point fixups" when extracting such links * (modifying the link target to point into the actual * extraction directory). So for the greatest cross-platform * consistency, we have to use the fake C: drive approach. */ static const utf16lechar prefix[6] = { cpu_to_le16('\\'), cpu_to_le16('?'), cpu_to_le16('?'), cpu_to_le16('\\'), cpu_to_le16('C'), cpu_to_le16(':'), }; /* Do not show \??\ in print name */ const size_t num_unprintable_chars = 4; link.symlink_flags = 0; link.substitute_name_nbytes = sizeof(prefix) + target_nbytes; link.substitute_name = alloca(link.substitute_name_nbytes); memcpy(link.substitute_name, prefix, sizeof(prefix)); memcpy(link.substitute_name + ARRAY_LEN(prefix), target, target_nbytes); link.print_name_nbytes = link.substitute_name_nbytes - (num_unprintable_chars * sizeof(utf16lechar)); link.print_name = link.substitute_name + num_unprintable_chars; } else { /* UNIX link target was relative. In this case we represent the * link as a symlink reparse point with SYMBOLIC_LINK_RELATIVE * set. This causes Windows to interpret the link relative to * the directory containing the reparse point file. */ link.symlink_flags = SYMBOLIC_LINK_RELATIVE; link.substitute_name_nbytes = target_nbytes; link.substitute_name = target; link.print_name_nbytes = target_nbytes; link.print_name = target; } /* Generate the reparse buffer. */ ret = make_link_reparse_point(&link, &rpbuf, &rpbuflen); if (ret) goto out_free_target; /* Save the reparse data with the inode. */ ret = WIMLIB_ERR_NOMEM; if (!inode_add_stream_with_data(inode, STREAM_TYPE_REPARSE_POINT, NO_STREAM_NAME, rpbuf.rpdata, rpbuflen - REPARSE_DATA_OFFSET, blob_table)) goto out_free_target; /* The inode is now a reparse point. */ inode->i_reparse_tag = link.rptag; inode->i_attributes &= ~FILE_ATTRIBUTE_NORMAL; inode->i_attributes |= FILE_ATTRIBUTE_REPARSE_POINT; ret = 0; out_free_target: FREE(target); return ret; } #endif /* !__WIN32__ */ wimlib-1.13.1/src/wimboot.c0000644000175000017500000010026113160354225012415 00000000000000/* * wimboot.c * * Support for creating WIMBoot pointer files. * * See http://technet.microsoft.com/en-us/library/dn594399.aspx for general * information about WIMBoot. * * Note that WIMBoot pointer files are actually implemented on top of the * Windows Overlay Filesystem filter (WOF). See wof.h for more info. */ /* * Copyright (C) 2014-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef __WIN32__ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib/win32_common.h" #include "wimlib/assert.h" #include "wimlib/blob_table.h" #include "wimlib/inode.h" #include "wimlib/error.h" #include "wimlib/util.h" #include "wimlib/wimboot.h" #include "wimlib/win32.h" #include "wimlib/wof.h" static HANDLE open_file(const wchar_t *device_name, DWORD desiredAccess) { return CreateFile(device_name, desiredAccess, FILE_SHARE_VALID_FLAGS, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); } static BOOL query_device(HANDLE h, DWORD code, void *out, DWORD out_size) { DWORD bytes_returned; return DeviceIoControl(h, code, NULL, 0, out, out_size, &bytes_returned, NULL); } /* * Gets partition and drive information for the specified path. * * @path * Absolute path which must begin with a drive letter. For example, if the * path is D:\install.wim, this function will query information about the * D: volume. * @part_info_ret * Partition info is returned here. * @drive_info_ret * Drive info is returned here. The contained partition info will not be * valid. * * Returns 0 on success, or a positive error code on failure. */ static int query_partition_and_disk_info(const wchar_t *path, PARTITION_INFORMATION_EX *part_info, DRIVE_LAYOUT_INFORMATION_EX *drive_info_ret) { wchar_t vol_name[] = L"\\\\.\\X:"; wchar_t disk_name[] = L"\\\\?\\PhysicalDriveXXXXXXXXXX"; HANDLE h = INVALID_HANDLE_VALUE; VOLUME_DISK_EXTENTS *extents = NULL; size_t extents_size; DRIVE_LAYOUT_INFORMATION_EX *drive_info = NULL; size_t drive_info_size; int ret; wimlib_assert(path[0] != L'\0' && path[1] == L':'); *(wcschr(vol_name, L'X')) = path[0]; h = open_file(vol_name, GENERIC_READ); if (h == INVALID_HANDLE_VALUE) { win32_error(GetLastError(), L"\"%ls\": Can't open volume device", vol_name); ret = WIMLIB_ERR_OPEN; goto out; } if (!query_device(h, IOCTL_DISK_GET_PARTITION_INFO_EX, part_info, sizeof(PARTITION_INFORMATION_EX))) { win32_error(GetLastError(), L"\"%ls\": Can't get partition info", vol_name); ret = WIMLIB_ERR_READ; goto out; } extents_size = sizeof(VOLUME_DISK_EXTENTS); for (;;) { extents_size += 4 * sizeof(DISK_EXTENT); extents = MALLOC(extents_size); if (!extents) { ret = WIMLIB_ERR_NOMEM; goto out; } if (query_device(h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, extents, extents_size)) break; if (GetLastError() != ERROR_MORE_DATA) { win32_error(GetLastError(), L"\"%ls\": Can't get volume extent info", vol_name); ret = WIMLIB_ERR_READ; goto out; } FREE(extents); } CloseHandle(h); h = INVALID_HANDLE_VALUE; if (extents->NumberOfDiskExtents != 1) { ERROR("\"%ls\": This volume has %"PRIu32" disk extents, " "but this code is untested for more than 1", vol_name, (u32)extents->NumberOfDiskExtents); ret = WIMLIB_ERR_UNSUPPORTED; goto out; } wsprintf(wcschr(disk_name, L'X'), L"%"PRIu32, extents->Extents[0].DiskNumber); h = open_file(disk_name, GENERIC_READ); if (h == INVALID_HANDLE_VALUE) { win32_error(GetLastError(), L"\"%ls\": Can't open disk device", disk_name); ret = WIMLIB_ERR_OPEN; goto out; } drive_info_size = sizeof(DRIVE_LAYOUT_INFORMATION_EX); for (;;) { drive_info_size += 4 * sizeof(PARTITION_INFORMATION_EX); drive_info = MALLOC(drive_info_size); if (!drive_info) { ret = WIMLIB_ERR_NOMEM; goto out; } if (query_device(h, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, drive_info, drive_info_size)) break; if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { win32_error(GetLastError(), L"\"%ls\": Can't get disk info", disk_name); ret = WIMLIB_ERR_READ; goto out; } FREE(drive_info); } *drive_info_ret = *drive_info; /* doesn't include partitions */ CloseHandle(h); h = INVALID_HANDLE_VALUE; if (drive_info->PartitionStyle != part_info->PartitionStyle) { ERROR("\"%ls\", \"%ls\": Inconsistent partition table type!", vol_name, disk_name); ret = WIMLIB_ERR_UNSUPPORTED; goto out; } if (part_info->PartitionStyle == PARTITION_STYLE_GPT) { STATIC_ASSERT(sizeof(part_info->Gpt.PartitionId) == sizeof(drive_info->Gpt.DiskId)); if (!memcmp(&part_info->Gpt.PartitionId, &drive_info->Gpt.DiskId, sizeof(drive_info->Gpt.DiskId))) { ERROR("\"%ls\", \"%ls\": Partition GUID is the " "same as the disk GUID???", vol_name, disk_name); ret = WIMLIB_ERR_UNSUPPORTED; goto out; } } if (part_info->PartitionStyle != PARTITION_STYLE_MBR && part_info->PartitionStyle != PARTITION_STYLE_GPT) { ERROR("\"%ls\": Unknown partition style 0x%08"PRIx32, vol_name, (u32)part_info->PartitionStyle); ret = WIMLIB_ERR_UNSUPPORTED; goto out; } ret = 0; out: FREE(extents); FREE(drive_info); if (h != INVALID_HANDLE_VALUE) CloseHandle(h); return ret; } /* * Calculate the size of WimOverlay.dat with one entry added. * * @old_hdr * Previous WimOverlay.dat contents, or NULL if file did not exist. * @new_entry_2_size * Size of entry_2 being added. * @size_ret * Size will be returned here. * * Returns 0 on success, or WIMLIB_ERR_UNSUPPORTED if size overflows 32 bits. */ static int calculate_wimoverlay_dat_size(const struct WimOverlay_dat_header *old_hdr, u32 new_entry_2_size, u32 *size_ret) { u64 size_64; u32 size; size_64 = sizeof(struct WimOverlay_dat_header); if (old_hdr) { for (u32 i = 0; i < old_hdr->num_entries; i++) { size_64 += sizeof(struct WimOverlay_dat_entry_1); size_64 += old_hdr->entry_1s[i].entry_2_length; } } size_64 += sizeof(struct WimOverlay_dat_entry_1); size_64 += new_entry_2_size; size = size_64; if (size_64 != size) return WIMLIB_ERR_UNSUPPORTED; *size_ret = size; return 0; } /* * Writes @size bytes of @contents to the named file @path. * * Returns 0 on success; WIMLIB_ERR_OPEN or WIMLIB_ERR_WRITE on failure. */ static int write_wimoverlay_dat(const wchar_t *path, const void *contents, u32 size) { HANDLE h; DWORD bytes_written; h = CreateFile(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) { win32_error(GetLastError(), L"\"%ls\": Can't open file for writing", path); return WIMLIB_ERR_OPEN; } SetLastError(0); if (!WriteFile(h, contents, size, &bytes_written, NULL) || bytes_written != size) { win32_error(GetLastError(), L"\"%ls\": Can't write file", path); CloseHandle(h); return WIMLIB_ERR_WRITE; } if (!CloseHandle(h)) { win32_error(GetLastError(), L"\"%ls\": Can't close handle", path); return WIMLIB_ERR_WRITE; } return 0; } /* * Generates the contents of WimOverlay.dat in memory, with one entry added. * * @buf * Buffer large enough to hold the new contents. * @old_hdr * Old contents of WimOverlay.dat, or NULL if it did not exist. * @wim_path * Absolute path to the WIM file. It must begin with a drive letter; for * example, D:\install.wim. * @wim_guid * GUID of the WIM, from the WIM header. * @image * Number of the image in the WIM to specify in the new entry. * @new_data_source_id * Data source ID to use for the new entry. * @part_info * Partition information for the WIM file. * @disk_info * Disk information for the WIM file. * @new_entry_2_size * Size, in bytes, of the new location information structure ('struct * WimOverlay_dat_entry_2'). * * Returns a pointer one past the last byte of @buf filled in. */ static u8 * fill_in_wimoverlay_dat(u8 *buf, const struct WimOverlay_dat_header *old_hdr, const wchar_t *wim_path, const u8 wim_guid[GUID_SIZE], int image, u64 new_data_source_id, const PARTITION_INFORMATION_EX *part_info, const DRIVE_LAYOUT_INFORMATION_EX *disk_info, u32 new_entry_2_size) { struct WimOverlay_dat_header *new_hdr; struct WimOverlay_dat_entry_1 *new_entry_1; struct WimOverlay_dat_entry_2 *new_entry_2; u32 entry_2_offset; u8 *p = buf; new_hdr = (struct WimOverlay_dat_header *)p; /* Fill in new header */ new_hdr->magic = WIMOVERLAY_DAT_MAGIC; new_hdr->wim_provider_version = WIM_PROVIDER_CURRENT_VERSION; new_hdr->unknown_0x08 = 0x00000028; new_hdr->num_entries = (old_hdr ? old_hdr->num_entries : 0) + 1; new_hdr->next_data_source_id = (old_hdr ? old_hdr->next_data_source_id : 0) + 1; p += sizeof(struct WimOverlay_dat_header); /* Copy WIM-specific information for old entries */ entry_2_offset = sizeof(struct WimOverlay_dat_header) + (new_hdr->num_entries * sizeof(struct WimOverlay_dat_entry_1)); if (old_hdr) { for (u32 i = 0; i < old_hdr->num_entries; i++) { new_entry_1 = (struct WimOverlay_dat_entry_1 *)p; p = mempcpy(p, &old_hdr->entry_1s[i], sizeof(struct WimOverlay_dat_entry_1)); new_entry_1->entry_2_offset = entry_2_offset; entry_2_offset += new_entry_1->entry_2_length; } } /* Generate WIM-specific information for new entry */ new_entry_1 = (struct WimOverlay_dat_entry_1 *)p; new_entry_1->data_source_id = new_data_source_id; new_entry_1->entry_2_offset = entry_2_offset; new_entry_1->entry_2_length = new_entry_2_size; new_entry_1->wim_type = WIM_BOOT_NOT_OS_WIM; new_entry_1->wim_index = image; STATIC_ASSERT(sizeof(new_entry_1->guid) == GUID_SIZE); copy_guid(new_entry_1->guid, wim_guid); p += sizeof(struct WimOverlay_dat_entry_1); /* Copy WIM location information for old entries */ if (old_hdr) { for (u32 i = 0; i < old_hdr->num_entries; i++) { wimlib_assert(new_hdr->entry_1s[i].entry_2_offset == p - buf); wimlib_assert(old_hdr->entry_1s[i].entry_2_length == new_hdr->entry_1s[i].entry_2_length); p = mempcpy(p, ((const u8 *)old_hdr + old_hdr->entry_1s[i].entry_2_offset), old_hdr->entry_1s[i].entry_2_length); } } /* Generate WIM location information for new entry */ new_entry_2 = (struct WimOverlay_dat_entry_2 *)p; new_entry_2->unknown_0x00 = 0x00000000; new_entry_2->unknown_0x04 = 0x00000000; new_entry_2->entry_2_length = new_entry_2_size; new_entry_2->unknown_0x0C = 0x00000000; new_entry_2->unknown_0x10 = 0x00000005; new_entry_2->unknown_0x14 = 0x00000001; new_entry_2->inner_struct_size = new_entry_2_size - 0x14; new_entry_2->unknown_0x1C = 0x00000005; new_entry_2->unknown_0x20 = 0x00000006; new_entry_2->unknown_0x24 = 0x00000000; new_entry_2->unknown_0x28 = 0x00000048; new_entry_2->unknown_0x2C = 0x00000000; new_entry_2->unknown_0x40 = 0x00000000; if (part_info->PartitionStyle == PARTITION_STYLE_MBR) { new_entry_2->partition.mbr.part_start_offset = part_info->StartingOffset.QuadPart; new_entry_2->partition.mbr.padding = 0; new_entry_2->partition_table_type = WIMOVERLAY_PARTITION_TYPE_MBR; new_entry_2->disk.mbr.disk_id = disk_info->Mbr.Signature; new_entry_2->disk.mbr.padding[0] = 0x00000000; new_entry_2->disk.mbr.padding[1] = 0x00000000; new_entry_2->disk.mbr.padding[2] = 0x00000000; } else { STATIC_ASSERT(sizeof(new_entry_2->partition.gpt.part_unique_guid) == sizeof(part_info->Gpt.PartitionId)); memcpy(new_entry_2->partition.gpt.part_unique_guid, &part_info->Gpt.PartitionId, sizeof(part_info->Gpt.PartitionId)); new_entry_2->partition_table_type = WIMOVERLAY_PARTITION_TYPE_GPT; STATIC_ASSERT(sizeof(new_entry_2->disk.gpt.disk_guid) == sizeof(disk_info->Gpt.DiskId)); memcpy(new_entry_2->disk.gpt.disk_guid, &disk_info->Gpt.DiskId, sizeof(disk_info->Gpt.DiskId)); STATIC_ASSERT(sizeof(new_entry_2->disk.gpt.disk_guid) == sizeof(new_entry_2->partition.gpt.part_unique_guid)); } new_entry_2->unknown_0x58[0] = 0x00000000; new_entry_2->unknown_0x58[1] = 0x00000000; new_entry_2->unknown_0x58[2] = 0x00000000; new_entry_2->unknown_0x58[3] = 0x00000000; wimlib_assert(wim_path[2] == L'\\'); return mempcpy(new_entry_2->wim_file_name, wim_path + 2, new_entry_2_size - sizeof(struct WimOverlay_dat_entry_2)); } /* * Prepares the new contents of WimOverlay.dat in memory, with one entry added. * * @old_hdr * Old contents of WimOverlay.dat, or NULL if it did not exist. * @wim_path * Absolute path to the WIM file. It must begin with a drive letter; for * example, D:\install.wim. * @wim_guid * GUID of the WIM, from the WIM header. * @image * Number of the image in the WIM to specify in the new entry. * @new_contents_ret * Location into which to return the new contents as a malloc()ed buffer on * success. * @new_contents_size_ret * Location into which to return the size, in bytes, of the new contents on * success. * @new_data_source_id_ret * Location into which to return the data source ID of the new entry on * success. * * Returns 0 on success, or a positive error code on failure. */ static int prepare_wimoverlay_dat(const struct WimOverlay_dat_header *old_hdr, const wchar_t *wim_path, const u8 wim_guid[GUID_SIZE], int image, void **new_contents_ret, u32 *new_contents_size_ret, u64 *new_data_source_id_ret) { int ret; PARTITION_INFORMATION_EX part_info; DRIVE_LAYOUT_INFORMATION_EX disk_info; u64 new_data_source_id; u32 new_entry_2_size; u32 new_contents_size; u8 *buf; u8 *end; ret = query_partition_and_disk_info(wim_path, &part_info, &disk_info); if (ret) return ret; new_data_source_id = old_hdr ? old_hdr->next_data_source_id : 0; new_entry_2_size = sizeof(struct WimOverlay_dat_entry_2) + ((wcslen(wim_path) - 2 + 1) * sizeof(wchar_t)); ret = calculate_wimoverlay_dat_size(old_hdr, new_entry_2_size, &new_contents_size); if (ret) return ret; buf = MALLOC(new_contents_size); if (!buf) return WIMLIB_ERR_NOMEM; end = fill_in_wimoverlay_dat(buf, old_hdr, wim_path, wim_guid, image, new_data_source_id, &part_info, &disk_info, new_entry_2_size); wimlib_assert(end - buf == new_contents_size); *new_contents_ret = buf; *new_contents_size_ret = new_contents_size; *new_data_source_id_ret = new_data_source_id; return 0; } /* * Reads and validates a WimOverlay.dat file. * * @path * Path to the WimOverlay.dat file, such as * C:\System Volume Information\WimOverlay.dat * @contents_ret * Location into which to return the contents as a malloc()ed buffer on * success. This can be cast to 'struct WimOverlay_dat_header', and its * contents are guaranteed to be valid. Alternatively, if the file does * not exist, NULL will be returned here. * * Returns 0 on success, or a positive error code on failure. */ static int read_wimoverlay_dat(const wchar_t *path, void **contents_ret) { HANDLE h; BY_HANDLE_FILE_INFORMATION info; int ret; void *contents; const struct WimOverlay_dat_header *hdr; DWORD bytes_read; bool already_retried = false; retry: h = open_file(path, GENERIC_READ); if (h == INVALID_HANDLE_VALUE) { DWORD err = GetLastError(); if (err == ERROR_FILE_NOT_FOUND) { *contents_ret = NULL; return 0; } if (err == ERROR_PATH_NOT_FOUND && func_RtlCreateSystemVolumeInformationFolder) { wchar_t volume_root_path[] = L"\\??\\X:\\"; *(wcschr(volume_root_path, L'X')) = path[0]; UNICODE_STRING str = { .Length = sizeof(volume_root_path) - sizeof(wchar_t), .MaximumLength = sizeof(volume_root_path), .Buffer = volume_root_path, }; NTSTATUS status; DWORD err2; status = (*func_RtlCreateSystemVolumeInformationFolder)(&str); err2 = RtlNtStatusToDosError(status); if (err2 == ERROR_SUCCESS) { if (!already_retried) { already_retried = true; goto retry; } } else { err = err2; } } win32_error(err, L"\"%ls\": Can't open for reading", path); return WIMLIB_ERR_OPEN; } if (!GetFileInformationByHandle(h, &info)) { win32_error(GetLastError(), L"\"%ls\": Can't query metadata", path); CloseHandle(h); return WIMLIB_ERR_STAT; } contents = NULL; if (!info.nFileSizeHigh) contents = MALLOC(info.nFileSizeLow); if (!contents) { ERROR("\"%ls\": File is too large to fit into memory", path); CloseHandle(h); return WIMLIB_ERR_NOMEM; } SetLastError(0); if (!ReadFile(h, contents, info.nFileSizeLow, &bytes_read, NULL) || bytes_read != info.nFileSizeLow) { win32_error(GetLastError(), L"\"%ls\": Can't read data", path); CloseHandle(h); ret = WIMLIB_ERR_READ; goto out_free_contents; } CloseHandle(h); if (info.nFileSizeLow < sizeof(struct WimOverlay_dat_header)) { ERROR("\"%ls\": File is unexpectedly small (only %"PRIu32" bytes)", path, (u32)info.nFileSizeLow); ret = WIMLIB_ERR_UNSUPPORTED; goto out_free_contents; } hdr = (const struct WimOverlay_dat_header *)contents; if (hdr->magic != WIMOVERLAY_DAT_MAGIC || hdr->wim_provider_version != WIM_PROVIDER_CURRENT_VERSION || hdr->unknown_0x08 != 0x00000028) { ERROR("\"%ls\": Header contains unexpected data:", path); if (wimlib_print_errors) { print_byte_field((const u8 *)hdr, sizeof(struct WimOverlay_dat_header), wimlib_error_file); fputc('\n', wimlib_error_file); } ret = WIMLIB_ERR_UNSUPPORTED; goto out_free_contents; } if ((u64)hdr->num_entries * sizeof(struct WimOverlay_dat_entry_1) > info.nFileSizeLow - sizeof(struct WimOverlay_dat_header)) { ERROR("\"%ls\": File is unexpectedly small " "(only %"PRIu32" bytes, but has %"PRIu32" entries)", path, (u32)info.nFileSizeLow, hdr->num_entries); ret = WIMLIB_ERR_UNSUPPORTED; goto out_free_contents; } for (u32 i = 0; i < hdr->num_entries; i++) { const struct WimOverlay_dat_entry_1 *entry_1; const struct WimOverlay_dat_entry_2 *entry_2; u32 wim_file_name_length; entry_1 = &hdr->entry_1s[i]; if (entry_1->data_source_id >= hdr->next_data_source_id) { ERROR("\"%ls\": value of next_data_source_id " "(0x%016"PRIx64") is unexpected, since entry " "%"PRIu32" has data source ID 0x%016"PRIx64, path, hdr->next_data_source_id, i, entry_1->data_source_id); ret = WIMLIB_ERR_UNSUPPORTED; goto out_free_contents; } if (((u64)entry_1->entry_2_offset + (u64)entry_1->entry_2_length) > info.nFileSizeLow) { ERROR("\"%ls\": entry %"PRIu32" (2) " "(data source ID 0x%016"PRIx64") " "overflows file", path, i, entry_1->data_source_id); ret = WIMLIB_ERR_UNSUPPORTED; goto out_free_contents; } if (entry_1->entry_2_length < sizeof(struct WimOverlay_dat_entry_2)) { ERROR("\"%ls\": entry %"PRIu32" (2) " "(data source ID 0x%016"PRIx64") " "is too short", path, i, entry_1->data_source_id); ret = WIMLIB_ERR_UNSUPPORTED; goto out_free_contents; } if (entry_1->entry_2_offset % 2 != 0) { ERROR("\"%ls\": entry %"PRIu32" (2) " "(data source ID 0x%016"PRIx64") " "is misaligned", path, i, entry_1->data_source_id); ret = WIMLIB_ERR_UNSUPPORTED; goto out_free_contents; } entry_2 = (const struct WimOverlay_dat_entry_2 *) ((const u8 *)hdr + entry_1->entry_2_offset); wim_file_name_length = entry_1->entry_2_length - sizeof(struct WimOverlay_dat_entry_2); if ((wim_file_name_length < 2 * sizeof(wchar_t)) || (wim_file_name_length % sizeof(wchar_t) != 0) || (wmemchr(entry_2->wim_file_name, L'\0', wim_file_name_length / sizeof(wchar_t)) != &entry_2->wim_file_name[wim_file_name_length / sizeof(wchar_t) - 1])) { ERROR("\"%ls\": entry %"PRIu32" (2) " "(data source ID 0x%016"PRIx64") " "has invalid WIM file name", path, i, entry_1->data_source_id); if (wimlib_print_errors) { print_byte_field((const u8 *)entry_2->wim_file_name, wim_file_name_length, wimlib_error_file); fputc('\n', wimlib_error_file); } ret = WIMLIB_ERR_UNSUPPORTED; goto out_free_contents; } if (entry_2->unknown_0x00 != 0x00000000 || entry_2->unknown_0x04 != 0x00000000 || entry_2->unknown_0x0C != 0x00000000 || entry_2->entry_2_length != entry_1->entry_2_length || entry_2->unknown_0x10 != 0x00000005 || entry_2->unknown_0x14 != 0x00000001 || entry_2->inner_struct_size != entry_1->entry_2_length - 0x14 || entry_2->unknown_0x1C != 0x00000005 || entry_2->unknown_0x20 != 0x00000006 || entry_2->unknown_0x24 != 0x00000000 || entry_2->unknown_0x28 != 0x00000048 || entry_2->unknown_0x2C != 0x00000000 || entry_2->unknown_0x40 != 0x00000000 || (entry_2->partition_table_type != WIMOVERLAY_PARTITION_TYPE_GPT && entry_2->partition_table_type != WIMOVERLAY_PARTITION_TYPE_MBR) || (entry_2->partition_table_type == WIMOVERLAY_PARTITION_TYPE_MBR && entry_2->partition.mbr.padding != 0) || (entry_2->partition_table_type == WIMOVERLAY_PARTITION_TYPE_GPT && entry_2->partition.mbr.padding == 0) || entry_2->unknown_0x58[0] != 0x00000000 || entry_2->unknown_0x58[1] != 0x00000000 || entry_2->unknown_0x58[2] != 0x00000000 || entry_2->unknown_0x58[3] != 0x00000000) { ERROR("\"%ls\": entry %"PRIu32" (2) " "(data source ID 0x%016"PRIx64") " "contains unexpected data!", path, i, entry_1->data_source_id); if (wimlib_print_errors) { print_byte_field((const u8 *)entry_2, entry_1->entry_2_length, wimlib_error_file); fputc('\n', wimlib_error_file); } ret = WIMLIB_ERR_UNSUPPORTED; goto out_free_contents; } } *contents_ret = contents; return 0; out_free_contents: FREE(contents); return ret; } /* * Update WimOverlay.dat manually in order to add a WIM data source to the * target volume. * * THIS CODE IS EXPERIMENTAL AS I HAD TO REVERSE ENGINEER THE FILE FORMAT! * * @path * Target drive. Must be a letter followed by a colon (e.g. D:). * @wim_path * Absolute path to the WIM file. It must begin with a drive letter; for * example, D:\install.wim. * @wim_guid * GUID of the WIM, from the WIM header. * @image * Number of the image in the WIM to specify in the new entry. * @data_source_id_ret * On success, the allocated data source ID is returned here. */ static int update_wimoverlay_manually(const wchar_t *drive, const wchar_t *wim_path, const u8 wim_guid[GUID_SIZE], int image, u64 *data_source_id_ret) { wchar_t path_main[] = L"A:\\System Volume Information\\WimOverlay.dat"; wchar_t path_backup[] = L"A:\\System Volume Information\\WimOverlay.backup"; wchar_t path_wimlib_backup[] = L"A:\\System Volume Information\\WimOverlay.wimlib_backup"; wchar_t path_new[] = L"A:\\System Volume Information\\WimOverlay.wimlib_new"; void *old_contents = NULL; void *new_contents = NULL; u32 new_contents_size = 0; u64 new_data_source_id = -1; int ret; wimlib_assert(drive[0] != L'\0' && drive[1] == L':' && drive[2] == L'\0'); path_main[0] = drive[0]; path_backup[0] = drive[0]; path_wimlib_backup[0] = drive[0]; path_new[0] = drive[0]; ret = read_wimoverlay_dat(path_main, &old_contents); if (ret) goto out; ret = prepare_wimoverlay_dat(old_contents, wim_path, wim_guid, image, &new_contents, &new_contents_size, &new_data_source_id); FREE(old_contents); if (ret) goto out; /* Write WimOverlay.wimlib_new */ ret = write_wimoverlay_dat(path_new, new_contents, new_contents_size); if (ret) goto out_free_new_contents; /* Write WimOverlay.backup */ ret = write_wimoverlay_dat(path_backup, new_contents, new_contents_size); if (ret) goto out_free_new_contents; if (old_contents) { /* Rename WimOverlay.dat => WimOverlay.wimlib_backup */ ret = win32_rename_replacement(path_main, path_wimlib_backup); if (ret) { ERROR_WITH_ERRNO("Can't rename \"%ls\" => \"%ls\"", path_main, path_wimlib_backup); ret = WIMLIB_ERR_RENAME; goto out_free_new_contents; } } /* Rename WimOverlay.wimlib_new => WimOverlay.dat */ ret = win32_rename_replacement(path_new, path_main); if (ret) { ERROR_WITH_ERRNO("Can't rename \"%ls\" => \"%ls\"", path_new, path_main); ret = WIMLIB_ERR_RENAME; } out_free_new_contents: FREE(new_contents); out: if (ret == WIMLIB_ERR_UNSUPPORTED) { ERROR("Please report to developer ("PACKAGE_BUGREPORT").\n" " If possible send the file \"%ls\".\n\n", path_main); } if (ret == 0) *data_source_id_ret = new_data_source_id; return ret; } /* * Allocate a WOF data source ID for a WIM file. * * @wim_path * Absolute path to the WIM file. This must include a drive letter and use * backslash path separators. * @wim_guid * GUID of the WIM, from the WIM header. * @image * Number of the image in the WIM being applied. * @target * Path to the target directory. * @data_source_id_ret * On success, an identifier for the backing WIM file will be returned * here. * * Returns 0 on success, or a positive error code on failure. */ int wimboot_alloc_data_source_id(const wchar_t *wim_path, const u8 wim_guid[GUID_SIZE], int image, const wchar_t *target, u64 *data_source_id_ret, bool *wof_running_ret) { tchar drive_path[7]; size_t wim_path_nchars; size_t wim_file_name_length; void *in; size_t insize; struct wof_external_info *wof_info; struct wim_provider_add_overlay_input *wim_info; HANDLE h; u64 data_source_id; DWORD bytes_returned; int ret; const wchar_t *prefix = L"\\??\\"; const size_t prefix_nchars = 4; bool tried_to_attach_wof = false; ret = win32_get_drive_path(target, drive_path); if (ret) return ret; wimlib_assert(!wcschr(wim_path, L'/')); wimlib_assert(wim_path[0] != L'\0' && wim_path[1] == L':'); wim_path_nchars = wcslen(wim_path); wim_file_name_length = sizeof(wchar_t) * (wim_path_nchars + prefix_nchars); insize = sizeof(struct wof_external_info) + sizeof(struct wim_provider_add_overlay_input) + wim_file_name_length; in = MALLOC(insize); if (!in) { ret = WIMLIB_ERR_NOMEM; goto out; } wof_info = (struct wof_external_info *)in; wof_info->version = WOF_CURRENT_VERSION; wof_info->provider = WOF_PROVIDER_WIM; wim_info = (struct wim_provider_add_overlay_input *)(wof_info + 1); wim_info->wim_type = WIM_BOOT_NOT_OS_WIM; wim_info->wim_index = image; wim_info->wim_file_name_offset = offsetof(struct wim_provider_add_overlay_input, wim_file_name); wim_info->wim_file_name_length = wim_file_name_length; wmemcpy(&wim_info->wim_file_name[0], prefix, prefix_nchars); wmemcpy(&wim_info->wim_file_name[prefix_nchars], wim_path, wim_path_nchars); retry_ioctl: h = open_file(drive_path, GENERIC_WRITE); if (h == INVALID_HANDLE_VALUE) { win32_error(GetLastError(), L"Failed to open \"%ls\"", drive_path + 4); ret = WIMLIB_ERR_OPEN; goto out_free_in; } if (!DeviceIoControl(h, FSCTL_ADD_OVERLAY, in, insize, &data_source_id, sizeof(data_source_id), &bytes_returned, NULL)) { DWORD err = GetLastError(); if (err == ERROR_INVALID_FUNCTION) { if (!tried_to_attach_wof) { CloseHandle(h); h = INVALID_HANDLE_VALUE; tried_to_attach_wof = true; if (win32_try_to_attach_wof(drive_path + 4)) goto retry_ioctl; } ret = WIMLIB_ERR_UNSUPPORTED; goto out_close_handle; } else { win32_error(err, L"Failed to add overlay source \"%ls\" " "to volume \"%ls\"", wim_path, drive_path + 4); ret = WIMLIB_ERR_WIMBOOT; goto out_close_handle; } } if (bytes_returned != sizeof(data_source_id)) { ret = WIMLIB_ERR_WIMBOOT; ERROR("Unexpected result size when adding " "overlay source \"%ls\" to volume \"%ls\"", wim_path, drive_path + 4); goto out_close_handle; } *wof_running_ret = true; *data_source_id_ret = data_source_id; ret = 0; out_close_handle: CloseHandle(h); out_free_in: FREE(in); out: if (ret == WIMLIB_ERR_UNSUPPORTED) { WARNING("WOF driver is not available; updating WimOverlay.dat directly."); ret = update_wimoverlay_manually(drive_path + 4, wim_path, wim_guid, image, data_source_id_ret); *wof_running_ret = false; } return ret; } /* * Set WIMBoot information on the specified file. * * This turns it into a reparse point that redirects accesses to it, to the * corresponding resource in the WIM archive. * * @h * Open handle to the file, with GENERIC_WRITE access. * @blob * The blob for the unnamed data stream of the file. * @data_source_id * Allocated identifier for the WIM data source on the destination volume. * @blob_table_hash * SHA-1 message digest of the WIM's blob table. * @wof_running * %true if the WOF driver appears to be available and working; %false if * not. * * Returns %true on success, or %false on failure with GetLastError() set. */ bool wimboot_set_pointer(HANDLE h, const struct blob_descriptor *blob, u64 data_source_id, const u8 blob_table_hash[SHA1_HASH_SIZE], bool wof_running) { DWORD bytes_returned; if (wof_running) { /* The WOF driver is running. We can create the reparse point * using FSCTL_SET_EXTERNAL_BACKING. */ unsigned int max_retries = 4; struct { struct wof_external_info wof_info; struct wim_provider_external_info wim_info; } in; retry: memset(&in, 0, sizeof(in)); in.wof_info.version = WOF_CURRENT_VERSION; in.wof_info.provider = WOF_PROVIDER_WIM; in.wim_info.version = WIM_PROVIDER_CURRENT_VERSION; in.wim_info.flags = 0; in.wim_info.data_source_id = data_source_id; copy_hash(in.wim_info.unnamed_data_stream_hash, blob->hash); /* blob_table_hash is not necessary */ if (!DeviceIoControl(h, FSCTL_SET_EXTERNAL_BACKING, &in, sizeof(in), NULL, 0, &bytes_returned, NULL)) { /* Try to track down sporadic errors */ if (wimlib_print_errors) { WARNING("FSCTL_SET_EXTERNAL_BACKING failed (err=%u); data was %zu bytes:", (u32)GetLastError(), sizeof(in)); print_byte_field((const u8 *)&in, sizeof(in), wimlib_error_file); putc('\n', wimlib_error_file); } if (--max_retries) { WARNING("Retrying after 100ms..."); Sleep(100); goto retry; } WARNING("Too many retries; returning failure"); return false; } } else { /* The WOF driver is running. We need to create the reparse * point manually. */ struct { struct { le32 rptag; le16 rpdatalen; le16 rpreserved; } hdr; struct wof_external_info wof_info; struct wim_provider_rpdata wim_info; } in; STATIC_ASSERT(sizeof(in) == 8 + sizeof(struct wof_external_info) + sizeof(struct wim_provider_rpdata)); in.hdr.rptag = WIM_IO_REPARSE_TAG_WOF; in.hdr.rpdatalen = sizeof(in) - sizeof(in.hdr); in.hdr.rpreserved = 0; in.wof_info.version = WOF_CURRENT_VERSION; in.wof_info.provider = WOF_PROVIDER_WIM; in.wim_info.version = 2; in.wim_info.flags = 0; in.wim_info.data_source_id = data_source_id; copy_hash(in.wim_info.unnamed_data_stream_hash, blob->hash); copy_hash(in.wim_info.blob_table_hash, blob_table_hash); in.wim_info.unnamed_data_stream_size = blob->size; in.wim_info.unnamed_data_stream_size_in_wim = blob->rdesc->size_in_wim; in.wim_info.unnamed_data_stream_offset_in_wim = blob->rdesc->offset_in_wim; if (!DeviceIoControl(h, FSCTL_SET_REPARSE_POINT, &in, sizeof(in), NULL, 0, &bytes_returned, NULL)) return false; /* We also need to create an unnamed data stream of the correct * size. Otherwise the file shows up as zero length. It can be * a sparse stream containing all zeroes; its contents * are unimportant. */ if (!DeviceIoControl(h, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &bytes_returned, NULL)) return false; if (!SetFilePointerEx(h, (LARGE_INTEGER){ .QuadPart = blob->size}, NULL, FILE_BEGIN)) return false; if (!SetEndOfFile(h)) return false; } return true; } #endif /* __WIN32__ */ wimlib-1.13.1/src/compress_parallel.c0000644000175000017500000003231013160354224014442 00000000000000/* * compress_parallel.c * * Compress chunks of data (parallel version). */ /* * Copyright (C) 2013 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef ENABLE_MULTITHREADED_COMPRESSION #include #include #include #include #include "wimlib/assert.h" #include "wimlib/chunk_compressor.h" #include "wimlib/error.h" #include "wimlib/list.h" #include "wimlib/util.h" struct message_queue { struct list_head list; pthread_mutex_t lock; pthread_cond_t msg_avail_cond; pthread_cond_t space_avail_cond; bool terminating; }; struct compressor_thread_data { pthread_t thread; struct message_queue *chunks_to_compress_queue; struct message_queue *compressed_chunks_queue; struct wimlib_compressor *compressor; }; #define MAX_CHUNKS_PER_MSG 16 struct message { u8 *uncompressed_chunks[MAX_CHUNKS_PER_MSG]; u8 *compressed_chunks[MAX_CHUNKS_PER_MSG]; u32 uncompressed_chunk_sizes[MAX_CHUNKS_PER_MSG]; u32 compressed_chunk_sizes[MAX_CHUNKS_PER_MSG]; size_t num_filled_chunks; size_t num_alloc_chunks; struct list_head list; bool complete; struct list_head submission_list; }; struct parallel_chunk_compressor { struct chunk_compressor base; struct message_queue chunks_to_compress_queue; struct message_queue compressed_chunks_queue; struct compressor_thread_data *thread_data; unsigned num_thread_data; unsigned num_started_threads; struct message *msgs; size_t num_messages; struct list_head available_msgs; struct list_head submitted_msgs; struct message *next_submit_msg; struct message *next_ready_msg; size_t next_chunk_idx; }; static int message_queue_init(struct message_queue *q) { if (pthread_mutex_init(&q->lock, NULL)) { ERROR_WITH_ERRNO("Failed to initialize mutex"); goto err; } if (pthread_cond_init(&q->msg_avail_cond, NULL)) { ERROR_WITH_ERRNO("Failed to initialize condition variable"); goto err_destroy_lock; } if (pthread_cond_init(&q->space_avail_cond, NULL)) { ERROR_WITH_ERRNO("Failed to initialize condition variable"); goto err_destroy_msg_avail_cond; } INIT_LIST_HEAD(&q->list); return 0; err_destroy_msg_avail_cond: pthread_cond_destroy(&q->msg_avail_cond); err_destroy_lock: pthread_mutex_destroy(&q->lock); err: return WIMLIB_ERR_NOMEM; } static void message_queue_destroy(struct message_queue *q) { if (q->list.next != NULL) { pthread_mutex_destroy(&q->lock); pthread_cond_destroy(&q->msg_avail_cond); pthread_cond_destroy(&q->space_avail_cond); } } static void message_queue_put(struct message_queue *q, struct message *msg) { pthread_mutex_lock(&q->lock); list_add_tail(&msg->list, &q->list); pthread_cond_signal(&q->msg_avail_cond); pthread_mutex_unlock(&q->lock); } static struct message * message_queue_get(struct message_queue *q) { struct message *msg; pthread_mutex_lock(&q->lock); while (list_empty(&q->list) && !q->terminating) pthread_cond_wait(&q->msg_avail_cond, &q->lock); if (!q->terminating) { msg = list_entry(q->list.next, struct message, list); list_del(&msg->list); } else msg = NULL; pthread_mutex_unlock(&q->lock); return msg; } static void message_queue_terminate(struct message_queue *q) { pthread_mutex_lock(&q->lock); q->terminating = true; pthread_cond_broadcast(&q->msg_avail_cond); pthread_mutex_unlock(&q->lock); } static int init_message(struct message *msg, size_t num_chunks, u32 out_chunk_size) { msg->num_alloc_chunks = num_chunks; for (size_t i = 0; i < num_chunks; i++) { msg->compressed_chunks[i] = MALLOC(out_chunk_size - 1); msg->uncompressed_chunks[i] = MALLOC(out_chunk_size); if (msg->compressed_chunks[i] == NULL || msg->uncompressed_chunks[i] == NULL) return WIMLIB_ERR_NOMEM; } return 0; } static void destroy_message(struct message *msg) { for (size_t i = 0; i < msg->num_alloc_chunks; i++) { FREE(msg->compressed_chunks[i]); FREE(msg->uncompressed_chunks[i]); } } static void free_messages(struct message *msgs, size_t num_messages) { if (msgs) { for (size_t i = 0; i < num_messages; i++) destroy_message(&msgs[i]); FREE(msgs); } } static struct message * allocate_messages(size_t count, size_t chunks_per_msg, u32 out_chunk_size) { struct message *msgs; msgs = CALLOC(count, sizeof(struct message)); if (msgs == NULL) return NULL; for (size_t i = 0; i < count; i++) { if (init_message(&msgs[i], chunks_per_msg, out_chunk_size)) { free_messages(msgs, count); return NULL; } } return msgs; } static void compress_chunks(struct message *msg, struct wimlib_compressor *compressor) { for (size_t i = 0; i < msg->num_filled_chunks; i++) { wimlib_assert(msg->uncompressed_chunk_sizes[i] != 0); msg->compressed_chunk_sizes[i] = wimlib_compress(msg->uncompressed_chunks[i], msg->uncompressed_chunk_sizes[i], msg->compressed_chunks[i], msg->uncompressed_chunk_sizes[i] - 1, compressor); } } static void * compressor_thread_proc(void *arg) { struct compressor_thread_data *params = arg; struct message *msg; while ((msg = message_queue_get(params->chunks_to_compress_queue)) != NULL) { compress_chunks(msg, params->compressor); message_queue_put(params->compressed_chunks_queue, msg); } return NULL; } static void parallel_chunk_compressor_destroy(struct chunk_compressor *_ctx) { struct parallel_chunk_compressor *ctx = (struct parallel_chunk_compressor *)_ctx; unsigned i; if (ctx == NULL) return; if (ctx->num_started_threads != 0) { message_queue_terminate(&ctx->chunks_to_compress_queue); for (i = 0; i < ctx->num_started_threads; i++) pthread_join(ctx->thread_data[i].thread, NULL); } message_queue_destroy(&ctx->chunks_to_compress_queue); message_queue_destroy(&ctx->compressed_chunks_queue); if (ctx->thread_data != NULL) for (i = 0; i < ctx->num_thread_data; i++) wimlib_free_compressor(ctx->thread_data[i].compressor); FREE(ctx->thread_data); free_messages(ctx->msgs, ctx->num_messages); FREE(ctx); } static void submit_compression_msg(struct parallel_chunk_compressor *ctx) { struct message *msg = ctx->next_submit_msg; msg->complete = false; list_add_tail(&msg->submission_list, &ctx->submitted_msgs); message_queue_put(&ctx->chunks_to_compress_queue, msg); ctx->next_submit_msg = NULL; } static void * parallel_chunk_compressor_get_chunk_buffer(struct chunk_compressor *_ctx) { struct parallel_chunk_compressor *ctx = (struct parallel_chunk_compressor *)_ctx; struct message *msg; if (ctx->next_submit_msg) { msg = ctx->next_submit_msg; } else { if (list_empty(&ctx->available_msgs)) return NULL; msg = list_entry(ctx->available_msgs.next, struct message, list); list_del(&msg->list); ctx->next_submit_msg = msg; msg->num_filled_chunks = 0; } return msg->uncompressed_chunks[msg->num_filled_chunks]; } static void parallel_chunk_compressor_signal_chunk_filled(struct chunk_compressor *_ctx, u32 usize) { struct parallel_chunk_compressor *ctx = (struct parallel_chunk_compressor *)_ctx; struct message *msg; wimlib_assert(usize > 0); wimlib_assert(usize <= ctx->base.out_chunk_size); wimlib_assert(ctx->next_submit_msg); msg = ctx->next_submit_msg; msg->uncompressed_chunk_sizes[msg->num_filled_chunks] = usize; if (++msg->num_filled_chunks == msg->num_alloc_chunks) submit_compression_msg(ctx); } static bool parallel_chunk_compressor_get_compression_result(struct chunk_compressor *_ctx, const void **cdata_ret, u32 *csize_ret, u32 *usize_ret) { struct parallel_chunk_compressor *ctx = (struct parallel_chunk_compressor *)_ctx; struct message *msg; if (ctx->next_submit_msg) submit_compression_msg(ctx); if (ctx->next_ready_msg) { msg = ctx->next_ready_msg; } else { if (list_empty(&ctx->submitted_msgs)) return false; while (!(msg = list_entry(ctx->submitted_msgs.next, struct message, submission_list))->complete) message_queue_get(&ctx->compressed_chunks_queue)->complete = true; ctx->next_ready_msg = msg; ctx->next_chunk_idx = 0; } if (msg->compressed_chunk_sizes[ctx->next_chunk_idx]) { *cdata_ret = msg->compressed_chunks[ctx->next_chunk_idx]; *csize_ret = msg->compressed_chunk_sizes[ctx->next_chunk_idx]; } else { *cdata_ret = msg->uncompressed_chunks[ctx->next_chunk_idx]; *csize_ret = msg->uncompressed_chunk_sizes[ctx->next_chunk_idx]; } *usize_ret = msg->uncompressed_chunk_sizes[ctx->next_chunk_idx]; if (++ctx->next_chunk_idx == msg->num_filled_chunks) { list_del(&msg->submission_list); list_add_tail(&msg->list, &ctx->available_msgs); ctx->next_ready_msg = NULL; } return true; } int new_parallel_chunk_compressor(int out_ctype, u32 out_chunk_size, unsigned num_threads, u64 max_memory, struct chunk_compressor **compressor_ret) { u64 approx_mem_required; size_t chunks_per_msg; size_t msgs_per_thread; struct parallel_chunk_compressor *ctx; unsigned i; int ret; unsigned desired_num_threads; wimlib_assert(out_chunk_size > 0); if (num_threads == 0) num_threads = get_available_cpus(); if (num_threads == 1) return -1; if (max_memory == 0) max_memory = get_available_memory(); desired_num_threads = num_threads; if (out_chunk_size < ((u32)1 << 23)) { /* Relatively small chunks. Use 2 messages per thread, each * with at least 2 chunks. Use more chunks per message if there * are lots of threads and/or the chunks are very small. */ chunks_per_msg = 2; chunks_per_msg += num_threads * (65536 / out_chunk_size) / 16; chunks_per_msg = max(chunks_per_msg, 2); chunks_per_msg = min(chunks_per_msg, MAX_CHUNKS_PER_MSG); msgs_per_thread = 2; } else { /* Big chunks: Just have one buffer per thread --- more would * just waste memory. */ chunks_per_msg = 1; msgs_per_thread = 1; } for (;;) { approx_mem_required = (u64)chunks_per_msg * (u64)msgs_per_thread * (u64)num_threads * (u64)out_chunk_size + out_chunk_size + 1000000 + num_threads * wimlib_get_compressor_needed_memory(out_ctype, out_chunk_size, 0); if (approx_mem_required <= max_memory) break; if (chunks_per_msg > 1) chunks_per_msg--; else if (msgs_per_thread > 1) msgs_per_thread--; else if (num_threads > 1) num_threads--; else break; } if (num_threads < desired_num_threads) { WARNING("Wanted to use %u threads, but limiting to %u " "to fit in available memory!", desired_num_threads, num_threads); } if (num_threads == 1) return -2; ret = WIMLIB_ERR_NOMEM; ctx = CALLOC(1, sizeof(*ctx)); if (ctx == NULL) goto err; ctx->base.out_ctype = out_ctype; ctx->base.out_chunk_size = out_chunk_size; ctx->base.destroy = parallel_chunk_compressor_destroy; ctx->base.get_chunk_buffer = parallel_chunk_compressor_get_chunk_buffer; ctx->base.signal_chunk_filled = parallel_chunk_compressor_signal_chunk_filled; ctx->base.get_compression_result = parallel_chunk_compressor_get_compression_result; ctx->num_thread_data = num_threads; ret = message_queue_init(&ctx->chunks_to_compress_queue); if (ret) goto err; ret = message_queue_init(&ctx->compressed_chunks_queue); if (ret) goto err; ret = WIMLIB_ERR_NOMEM; ctx->thread_data = CALLOC(num_threads, sizeof(ctx->thread_data[0])); if (ctx->thread_data == NULL) goto err; for (i = 0; i < num_threads; i++) { struct compressor_thread_data *dat; dat = &ctx->thread_data[i]; dat->chunks_to_compress_queue = &ctx->chunks_to_compress_queue; dat->compressed_chunks_queue = &ctx->compressed_chunks_queue; ret = wimlib_create_compressor(out_ctype, out_chunk_size, WIMLIB_COMPRESSOR_FLAG_DESTRUCTIVE, &dat->compressor); if (ret) goto err; } for (ctx->num_started_threads = 0; ctx->num_started_threads < num_threads; ctx->num_started_threads++) { ret = pthread_create(&ctx->thread_data[ctx->num_started_threads].thread, NULL, compressor_thread_proc, &ctx->thread_data[ctx->num_started_threads]); if (ret) { errno = ret; WARNING_WITH_ERRNO("Failed to create compressor thread %u of %u", ctx->num_started_threads + 1, num_threads); ret = WIMLIB_ERR_NOMEM; if (ctx->num_started_threads >= 2) break; goto err; } } ctx->base.num_threads = ctx->num_started_threads; ret = WIMLIB_ERR_NOMEM; ctx->num_messages = ctx->num_started_threads * msgs_per_thread; ctx->msgs = allocate_messages(ctx->num_messages, chunks_per_msg, out_chunk_size); if (ctx->msgs == NULL) goto err; INIT_LIST_HEAD(&ctx->available_msgs); for (size_t i = 0; i < ctx->num_messages; i++) list_add_tail(&ctx->msgs[i].list, &ctx->available_msgs); INIT_LIST_HEAD(&ctx->submitted_msgs); *compressor_ret = &ctx->base; return 0; err: parallel_chunk_compressor_destroy(&ctx->base); return ret; } #endif /* ENABLE_MULTITHREADED_COMPRESSION */ wimlib-1.13.1/src/delete_image.c0000644000175000017500000000522613160354224013345 00000000000000/* * delete_image.c */ /* * Copyright (C) 2012-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include "wimlib.h" #include "wimlib/dentry.h" #include "wimlib/metadata.h" #include "wimlib/wim.h" #include "wimlib/xml.h" /* Internal method for single-image deletion. This doesn't set the * image_deletion_occurred' flag on the WIMStruct. */ int delete_wim_image(WIMStruct *wim, int image) { int ret; struct wim_image_metadata *imd; /* Load the metadata for the image to be deleted. This is necessary * because blobs referenced by files in the image need to have their * reference counts decremented. */ ret = select_wim_image(wim, image); if (ret) return ret; /* Release the files and decrement the reference counts of the blobs * they reference. */ imd = wim->image_metadata[image - 1]; free_dentry_tree(imd->root_dentry, wim->blob_table); imd->root_dentry = NULL; /* Deselect the image and release its metadata. */ deselect_current_wim_image(wim); put_image_metadata(imd); /* Remove the empty slot from the image metadata array. */ memmove(&wim->image_metadata[image - 1], &wim->image_metadata[image], (wim->hdr.image_count - image) * sizeof(wim->image_metadata[0])); /* Decrement the image count. */ wim->hdr.image_count--; /* Remove the image from the XML information. */ xml_delete_image(wim->xml_info, image); /* Fix the boot index. */ if (wim->hdr.boot_idx == image) wim->hdr.boot_idx = 0; else if (wim->hdr.boot_idx > image) wim->hdr.boot_idx--; return 0; } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_delete_image(WIMStruct *wim, int image) { int ret; int first, last; if (image == WIMLIB_ALL_IMAGES) { /* Deleting all images */ last = wim->hdr.image_count; first = 1; } else { /* Deleting one image */ last = image; first = image; } for (image = last; image >= first; image--) { ret = delete_wim_image(wim, image); if (ret) return ret; wim->image_deletion_occurred = 1; } return 0; } wimlib-1.13.1/src/iterate_dir.c0000644000175000017500000001674113272231267013245 00000000000000/* * iterate_dir.c * * Iterate through files in a WIM image. * This is the stable API; internal code can just use for_dentry_in_tree(). */ /* * Copyright (C) 2013-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib.h" #include "wimlib/blob_table.h" #include "wimlib/dentry.h" #include "wimlib/encoding.h" #include "wimlib/metadata.h" #include "wimlib/object_id.h" #include "wimlib/paths.h" #include "wimlib/security.h" #include "wimlib/timestamp.h" #include "wimlib/unix_data.h" #include "wimlib/util.h" #include "wimlib/wim.h" static int stream_to_wimlib_stream_entry(const struct wim_inode *inode, const struct wim_inode_stream *strm, struct wimlib_stream_entry *wstream, const struct blob_table *blob_table, int flags) { const struct blob_descriptor *blob; const u8 *hash; if (stream_is_named(strm)) { int ret; ret = utf16le_get_tstr(strm->stream_name, utf16le_len_bytes(strm->stream_name), &wstream->stream_name, NULL); if (ret) return ret; } blob = stream_blob(strm, blob_table); if (blob) { blob_to_wimlib_resource_entry(blob, &wstream->resource); } else if (!is_zero_hash((hash = stream_hash(strm)))) { if (flags & WIMLIB_ITERATE_DIR_TREE_FLAG_RESOURCES_NEEDED) return blob_not_found_error(inode, hash); copy_hash(wstream->resource.sha1_hash, hash); wstream->resource.is_missing = 1; } return 0; } static int get_default_stream_type(const struct wim_inode *inode) { if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) return STREAM_TYPE_EFSRPC_RAW_DATA; if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) return STREAM_TYPE_REPARSE_POINT; return STREAM_TYPE_DATA; } static int init_wimlib_dentry(struct wimlib_dir_entry *wdentry, struct wim_dentry *dentry, WIMStruct *wim, int flags) { int ret; const struct wim_inode *inode = dentry->d_inode; const struct wim_inode_stream *strm; struct wimlib_unix_data unix_data; const void *object_id; u32 object_id_len; ret = utf16le_get_tstr(dentry->d_name, dentry->d_name_nbytes, &wdentry->filename, NULL); if (ret) return ret; ret = utf16le_get_tstr(dentry->d_short_name, dentry->d_short_name_nbytes, &wdentry->dos_name, NULL); if (ret) return ret; ret = calculate_dentry_full_path(dentry); if (ret) return ret; wdentry->full_path = dentry->d_full_path; for (struct wim_dentry *d = dentry; !dentry_is_root(d); d = d->d_parent) wdentry->depth++; if (inode_has_security_descriptor(inode)) { struct wim_security_data *sd; sd = wim_get_current_security_data(wim); wdentry->security_descriptor = sd->descriptors[inode->i_security_id]; wdentry->security_descriptor_size = sd->sizes[inode->i_security_id]; } wdentry->reparse_tag = inode->i_reparse_tag; wdentry->num_links = inode->i_nlink; wdentry->attributes = inode->i_attributes; wdentry->hard_link_group_id = inode->i_ino; wim_timestamp_to_wimlib_timespec(inode->i_creation_time, &wdentry->creation_time, &wdentry->creation_time_high); wim_timestamp_to_wimlib_timespec(inode->i_last_write_time, &wdentry->last_write_time, &wdentry->last_write_time_high); wim_timestamp_to_wimlib_timespec(inode->i_last_access_time, &wdentry->last_access_time, &wdentry->last_access_time_high); if (inode_get_unix_data(inode, &unix_data)) { wdentry->unix_uid = unix_data.uid; wdentry->unix_gid = unix_data.gid; wdentry->unix_mode = unix_data.mode; wdentry->unix_rdev = unix_data.rdev; } object_id = inode_get_object_id(inode, &object_id_len); if (unlikely(object_id != NULL)) { memcpy(&wdentry->object_id, object_id, min(object_id_len, sizeof(wdentry->object_id))); } strm = inode_get_unnamed_stream(inode, get_default_stream_type(inode)); if (strm) { ret = stream_to_wimlib_stream_entry(inode, strm, &wdentry->streams[0], wim->blob_table, flags); if (ret) return ret; } for (unsigned i = 0; i < inode->i_num_streams; i++) { strm = &inode->i_streams[i]; if (!stream_is_named_data_stream(strm)) continue; wdentry->num_named_streams++; ret = stream_to_wimlib_stream_entry(inode, strm, &wdentry->streams[ wdentry->num_named_streams], wim->blob_table, flags); if (ret) return ret; } return 0; } static void free_wimlib_dentry(struct wimlib_dir_entry *wdentry) { utf16le_put_tstr(wdentry->filename); utf16le_put_tstr(wdentry->dos_name); for (unsigned i = 1; i <= wdentry->num_named_streams; i++) utf16le_put_tstr(wdentry->streams[i].stream_name); FREE(wdentry); } static int do_iterate_dir_tree(WIMStruct *wim, struct wim_dentry *dentry, int flags, wimlib_iterate_dir_tree_callback_t cb, void *user_ctx) { struct wimlib_dir_entry *wdentry; int ret = WIMLIB_ERR_NOMEM; wdentry = CALLOC(1, sizeof(struct wimlib_dir_entry) + (1 + dentry->d_inode->i_num_streams) * sizeof(struct wimlib_stream_entry)); if (wdentry == NULL) goto out; ret = init_wimlib_dentry(wdentry, dentry, wim, flags); if (ret) goto out_free_wimlib_dentry; if (!(flags & WIMLIB_ITERATE_DIR_TREE_FLAG_CHILDREN)) { ret = (*cb)(wdentry, user_ctx); if (ret) goto out_free_wimlib_dentry; } if (flags & (WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE | WIMLIB_ITERATE_DIR_TREE_FLAG_CHILDREN)) { struct wim_dentry *child; ret = 0; for_dentry_child(child, dentry) { ret = do_iterate_dir_tree(wim, child, flags & ~WIMLIB_ITERATE_DIR_TREE_FLAG_CHILDREN, cb, user_ctx); if (ret) break; } } out_free_wimlib_dentry: FREE(dentry->d_full_path); dentry->d_full_path = NULL; free_wimlib_dentry(wdentry); out: return ret; } struct image_iterate_dir_tree_ctx { const tchar *path; int flags; wimlib_iterate_dir_tree_callback_t cb; void *user_ctx; }; static int image_do_iterate_dir_tree(WIMStruct *wim) { struct image_iterate_dir_tree_ctx *ctx = wim->private; struct wim_dentry *dentry; dentry = get_dentry(wim, ctx->path, WIMLIB_CASE_PLATFORM_DEFAULT); if (dentry == NULL) return WIMLIB_ERR_PATH_DOES_NOT_EXIST; return do_iterate_dir_tree(wim, dentry, ctx->flags, ctx->cb, ctx->user_ctx); } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_iterate_dir_tree(WIMStruct *wim, int image, const tchar *_path, int flags, wimlib_iterate_dir_tree_callback_t cb, void *user_ctx) { tchar *path; int ret; if (flags & ~(WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE | WIMLIB_ITERATE_DIR_TREE_FLAG_CHILDREN | WIMLIB_ITERATE_DIR_TREE_FLAG_RESOURCES_NEEDED)) return WIMLIB_ERR_INVALID_PARAM; path = canonicalize_wim_path(_path); if (path == NULL) return WIMLIB_ERR_NOMEM; ret = wim_checksum_unhashed_blobs(wim); if (ret) return ret; struct image_iterate_dir_tree_ctx ctx = { .path = path, .flags = flags, .cb = cb, .user_ctx = user_ctx, }; wim->private = &ctx; ret = for_image(wim, image, image_do_iterate_dir_tree); FREE(path); return ret; } wimlib-1.13.1/src/xml_windows.c0000644000175000017500000007520013160354225013313 00000000000000/* * xml_windows.c * * Set Windows-specific metadata in a WIM file's XML document based on the image * contents. */ /* * Copyright (C) 2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include "wimlib.h" #include "wimlib/blob_table.h" #include "wimlib/dentry.h" #include "wimlib/encoding.h" #include "wimlib/endianness.h" #include "wimlib/error.h" #include "wimlib/metadata.h" #include "wimlib/registry.h" #include "wimlib/wim.h" #include "wimlib/xml_windows.h" /* Context for a call to set_windows_specific_info() */ struct windows_info_ctx { WIMStruct *wim; int image; bool oom_encountered; bool debug_enabled; }; /* For debugging purposes, the environmental variable WIMLIB_DEBUG_XML_INFO can * be set to enable messages about certain things not being as expected in the * registry or other files used as information sources. */ #define XML_WARN(format, ...) \ if (ctx->debug_enabled) \ WARNING(format, ##__VA_ARGS__) /* Set a property in the XML document, with error checking. */ static void set_string_property(struct windows_info_ctx *ctx, const tchar *name, const tchar *value) { int ret = wimlib_set_image_property(ctx->wim, ctx->image, name, value); if (likely(!ret)) return; ctx->oom_encountered |= (ret == WIMLIB_ERR_NOMEM); WARNING("Failed to set image property \"%"TS"\" to value " "\"%"TS"\": %"TS, name, value, wimlib_get_error_string(ret)); } /* Set a property in the XML document, with error checking. */ static void set_number_property(struct windows_info_ctx *ctx, const tchar *name, s64 value) { tchar buffer[32]; tsprintf(buffer, T("%"PRIi64""), value); set_string_property(ctx, name, buffer); } /* Check the result of a registry hive operation. If unsuccessful, possibly * print debugging information. Return true iff successful. */ static bool check_hive_status(struct windows_info_ctx *ctx, enum hive_status status, const tchar *key, const tchar *value) { if (likely(status == HIVE_OK)) return true; ctx->oom_encountered |= (status == HIVE_OUT_OF_MEMORY); XML_WARN("%s; key=%"TS" value=%"TS, hive_status_to_string(status), (key ? key : T("(null)")), (value ? value : T("(null)"))); return false; } static bool is_registry_valid(struct windows_info_ctx *ctx, const void *hive_mem, size_t hive_size) { enum hive_status status; status = hive_validate(hive_mem, hive_size); return check_hive_status(ctx, status, NULL, NULL); } static bool get_string_from_registry(struct windows_info_ctx *ctx, const struct regf *regf, const tchar *key_name, const tchar *value_name, tchar **value_ret) { enum hive_status status; status = hive_get_string(regf, key_name, value_name, value_ret); return check_hive_status(ctx, status, key_name, value_name); } static bool get_number_from_registry(struct windows_info_ctx *ctx, const struct regf *regf, const tchar *key_name, const tchar *value_name, s64 *value_ret) { enum hive_status status; status = hive_get_number(regf, key_name, value_name, value_ret); return check_hive_status(ctx, status, key_name, value_name); } static bool list_subkeys_in_registry(struct windows_info_ctx *ctx, const struct regf *regf, const tchar *key_name, tchar ***subkeys_ret) { enum hive_status status; status = hive_list_subkeys(regf, key_name, subkeys_ret); return check_hive_status(ctx, status, key_name, NULL); } /* Copy a string value from a registry hive to the XML document. */ static void copy_registry_string(struct windows_info_ctx *ctx, const struct regf *regf, const tchar *key_name, const tchar *value_name, const tchar *property_name) { tchar *string; if (get_string_from_registry(ctx, regf, key_name, value_name, &string)) { set_string_property(ctx, property_name, string); FREE(string); } } /* A table that map Windows language IDs, sorted numerically, to their language * names. It was generated by tools/generate_language_id_map.c. */ static const struct { u16 id; u16 name_start_offset; } language_id_map[452] = { {0x0000, 0}, {0x0001, 6}, {0x0002, 12}, {0x0003, 18}, {0x0004, 24}, {0x0005, 30}, {0x0006, 36}, {0x0007, 42}, {0x0008, 48}, {0x0009, 54}, {0x000a, 60}, {0x000b, 66}, {0x000c, 72}, {0x000d, 78}, {0x000e, 84}, {0x000f, 90}, {0x0010, 96}, {0x0011, 102}, {0x0012, 108}, {0x0013, 114}, {0x0014, 120}, {0x0015, 126}, {0x0016, 132}, {0x0017, 138}, {0x0018, 144}, {0x0019, 150}, {0x001a, 156}, {0x001b, 162}, {0x001c, 168}, {0x001d, 174}, {0x001e, 180}, {0x001f, 186}, {0x0020, 192}, {0x0021, 198}, {0x0022, 204}, {0x0023, 210}, {0x0024, 216}, {0x0025, 222}, {0x0026, 228}, {0x0027, 234}, {0x0028, 240}, {0x0029, 251}, {0x002a, 257}, {0x002b, 263}, {0x002c, 269}, {0x002d, 280}, {0x002e, 286}, {0x002f, 293}, {0x0030, 299}, {0x0031, 305}, {0x0032, 311}, {0x0033, 317}, {0x0034, 323}, {0x0035, 329}, {0x0036, 335}, {0x0037, 341}, {0x0038, 347}, {0x0039, 353}, {0x003a, 359}, {0x003b, 365}, {0x003c, 371}, {0x003d, 377}, {0x003e, 384}, {0x003f, 390}, {0x0040, 396}, {0x0041, 402}, {0x0042, 408}, {0x0043, 414}, {0x0044, 425}, {0x0045, 431}, {0x0046, 437}, {0x0047, 443}, {0x0048, 449}, {0x0049, 455}, {0x004a, 461}, {0x004b, 467}, {0x004c, 473}, {0x004d, 479}, {0x004e, 485}, {0x004f, 491}, {0x0050, 497}, {0x0051, 503}, {0x0052, 509}, {0x0053, 515}, {0x0054, 521}, {0x0055, 527}, {0x0056, 533}, {0x0057, 539}, {0x0058, 546}, {0x0059, 553}, {0x005a, 564}, {0x005b, 571}, {0x005c, 577}, {0x005d, 589}, {0x005e, 600}, {0x005f, 606}, {0x0060, 618}, {0x0061, 629}, {0x0062, 635}, {0x0063, 641}, {0x0064, 647}, {0x0065, 654}, {0x0066, 660}, {0x0067, 667}, {0x0068, 678}, {0x0069, 689}, {0x006a, 696}, {0x006b, 702}, {0x006c, 709}, {0x006d, 716}, {0x006e, 722}, {0x006f, 728}, {0x0070, 734}, {0x0071, 740}, {0x0072, 746}, {0x0073, 752}, {0x0074, 758}, {0x0075, 764}, {0x0076, 771}, {0x0077, 778}, {0x0078, 784}, {0x0079, 790}, {0x007a, 798}, {0x007c, 805}, {0x007e, 812}, {0x007f, 818}, {0x0080, 819}, {0x0081, 825}, {0x0082, 831}, {0x0083, 837}, {0x0084, 843}, {0x0085, 850}, {0x0086, 857}, {0x0087, 869}, {0x0088, 875}, {0x008c, 881}, {0x0091, 888}, {0x0092, 894}, {0x0400, 905}, {0x0401, 911}, {0x0402, 917}, {0x0403, 923}, {0x0404, 929}, {0x0405, 935}, {0x0406, 941}, {0x0407, 947}, {0x0408, 953}, {0x0409, 959}, {0x040a, 965}, {0x040b, 978}, {0x040c, 984}, {0x040d, 990}, {0x040e, 996}, {0x040f, 1002}, {0x0410, 1008}, {0x0411, 1014}, {0x0412, 1020}, {0x0413, 1026}, {0x0414, 1032}, {0x0415, 1038}, {0x0416, 1044}, {0x0417, 1050}, {0x0418, 1056}, {0x0419, 1062}, {0x041a, 1068}, {0x041b, 1074}, {0x041c, 1080}, {0x041d, 1086}, {0x041e, 1092}, {0x041f, 1098}, {0x0420, 1104}, {0x0421, 1110}, {0x0422, 1116}, {0x0423, 1122}, {0x0424, 1128}, {0x0425, 1134}, {0x0426, 1140}, {0x0427, 1146}, {0x0428, 1152}, {0x0429, 1163}, {0x042a, 1169}, {0x042b, 1175}, {0x042c, 1181}, {0x042d, 1192}, {0x042e, 1198}, {0x042f, 1205}, {0x0430, 1211}, {0x0431, 1217}, {0x0432, 1223}, {0x0433, 1229}, {0x0434, 1235}, {0x0435, 1241}, {0x0436, 1247}, {0x0437, 1253}, {0x0438, 1259}, {0x0439, 1265}, {0x043a, 1271}, {0x043b, 1277}, {0x043d, 1283}, {0x043e, 1290}, {0x043f, 1296}, {0x0440, 1302}, {0x0441, 1308}, {0x0442, 1314}, {0x0443, 1320}, {0x0444, 1331}, {0x0445, 1337}, {0x0446, 1343}, {0x0447, 1349}, {0x0448, 1355}, {0x0449, 1361}, {0x044a, 1367}, {0x044b, 1373}, {0x044c, 1379}, {0x044d, 1385}, {0x044e, 1391}, {0x044f, 1397}, {0x0450, 1403}, {0x0451, 1409}, {0x0452, 1415}, {0x0453, 1421}, {0x0454, 1427}, {0x0455, 1433}, {0x0456, 1439}, {0x0457, 1445}, {0x0458, 1452}, {0x0459, 1459}, {0x045a, 1470}, {0x045b, 1477}, {0x045c, 1483}, {0x045d, 1495}, {0x045e, 1506}, {0x045f, 1512}, {0x0460, 1524}, {0x0461, 1535}, {0x0462, 1541}, {0x0463, 1547}, {0x0464, 1553}, {0x0465, 1560}, {0x0466, 1566}, {0x0467, 1573}, {0x0468, 1579}, {0x0469, 1590}, {0x046a, 1597}, {0x046b, 1603}, {0x046c, 1610}, {0x046d, 1617}, {0x046e, 1623}, {0x046f, 1629}, {0x0470, 1635}, {0x0471, 1641}, {0x0472, 1647}, {0x0473, 1653}, {0x0474, 1659}, {0x0475, 1665}, {0x0476, 1672}, {0x0477, 1679}, {0x0478, 1685}, {0x0479, 1691}, {0x047a, 1699}, {0x047c, 1706}, {0x047e, 1713}, {0x0480, 1719}, {0x0481, 1725}, {0x0482, 1731}, {0x0483, 1737}, {0x0484, 1743}, {0x0485, 1750}, {0x0486, 1757}, {0x0487, 1769}, {0x0488, 1775}, {0x048c, 1781}, {0x0491, 1788}, {0x0492, 1794}, {0x0501, 1805}, {0x05fe, 1814}, {0x0800, 1824}, {0x0801, 1830}, {0x0803, 1836}, {0x0804, 1851}, {0x0807, 1857}, {0x0809, 1863}, {0x080a, 1869}, {0x080c, 1875}, {0x0810, 1881}, {0x0813, 1887}, {0x0814, 1893}, {0x0816, 1899}, {0x0818, 1905}, {0x0819, 1911}, {0x081a, 1917}, {0x081d, 1928}, {0x0820, 1934}, {0x082c, 1940}, {0x082e, 1951}, {0x0832, 1958}, {0x083b, 1964}, {0x083c, 1970}, {0x083e, 1976}, {0x0843, 1982}, {0x0845, 1993}, {0x0846, 1999}, {0x0849, 2010}, {0x0850, 2016}, {0x0859, 2027}, {0x085d, 2038}, {0x085f, 2049}, {0x0860, 2061}, {0x0861, 2072}, {0x0867, 2078}, {0x086b, 2089}, {0x0873, 2096}, {0x0901, 2102}, {0x09ff, 2116}, {0x0c00, 2126}, {0x0c01, 2132}, {0x0c04, 2138}, {0x0c07, 2144}, {0x0c09, 2150}, {0x0c0a, 2156}, {0x0c0c, 2162}, {0x0c1a, 2168}, {0x0c3b, 2179}, {0x0c50, 2185}, {0x0c51, 2196}, {0x0c6b, 2202}, {0x1000, 2209}, {0x1001, 2220}, {0x1004, 2226}, {0x1007, 2232}, {0x1009, 2238}, {0x100a, 2244}, {0x100c, 2250}, {0x101a, 2256}, {0x103b, 2262}, {0x105f, 2269}, {0x1401, 2281}, {0x1404, 2287}, {0x1407, 2293}, {0x1409, 2299}, {0x140a, 2305}, {0x140c, 2311}, {0x141a, 2317}, {0x143b, 2328}, {0x1801, 2335}, {0x1809, 2341}, {0x180a, 2347}, {0x180c, 2353}, {0x181a, 2359}, {0x183b, 2370}, {0x1c01, 2377}, {0x1c09, 2383}, {0x1c0a, 2389}, {0x1c0c, 2395}, {0x1c1a, 2402}, {0x1c3b, 2413}, {0x2000, 2420}, {0x2001, 2426}, {0x2009, 2432}, {0x200a, 2438}, {0x200c, 2444}, {0x201a, 2450}, {0x203b, 2461}, {0x2400, 2468}, {0x2401, 2474}, {0x2409, 2480}, {0x240a, 2487}, {0x240c, 2493}, {0x241a, 2499}, {0x243b, 2510}, {0x2800, 2517}, {0x2801, 2523}, {0x2809, 2529}, {0x280a, 2535}, {0x280c, 2541}, {0x281a, 2547}, {0x2c00, 2558}, {0x2c01, 2564}, {0x2c09, 2570}, {0x2c0a, 2576}, {0x2c0c, 2582}, {0x2c1a, 2588}, {0x3000, 2599}, {0x3001, 2605}, {0x3009, 2611}, {0x300a, 2617}, {0x300c, 2623}, {0x301a, 2629}, {0x3400, 2640}, {0x3401, 2646}, {0x3409, 2652}, {0x340a, 2658}, {0x340c, 2664}, {0x3800, 2670}, {0x3801, 2676}, {0x3809, 2682}, {0x380a, 2688}, {0x380c, 2694}, {0x3c00, 2700}, {0x3c01, 2706}, {0x3c09, 2712}, {0x3c0a, 2718}, {0x3c0c, 2724}, {0x4000, 2730}, {0x4001, 2736}, {0x4009, 2742}, {0x400a, 2748}, {0x4400, 2754}, {0x4409, 2760}, {0x440a, 2766}, {0x4800, 2772}, {0x4809, 2778}, {0x480a, 2784}, {0x4c00, 2790}, {0x4c0a, 2796}, {0x500a, 2802}, {0x540a, 2808}, {0x580a, 2814}, {0x5c0a, 2821}, {0x641a, 2827}, {0x681a, 2838}, {0x6c1a, 2849}, {0x701a, 2860}, {0x703b, 2871}, {0x742c, 2878}, {0x743b, 2889}, {0x7804, 2896}, {0x7814, 2902}, {0x781a, 2908}, {0x782c, 2919}, {0x783b, 2930}, {0x7843, 2937}, {0x7850, 2948}, {0x785d, 2954}, {0x785f, 2965}, {0x7c04, 2977}, {0x7c14, 2983}, {0x7c1a, 2989}, {0x7c28, 3000}, {0x7c2e, 3011}, {0x7c3b, 3018}, {0x7c43, 3025}, {0x7c46, 3036}, {0x7c50, 3047}, {0x7c59, 3058}, {0x7c5c, 3069}, {0x7c5d, 3081}, {0x7c5f, 3092}, {0x7c67, 3104}, {0x7c68, 3115}, {0x7c86, 3126}, {0x7c92, 3138}, }; /* All the language names; generated by tools/generate_language_id_map.c. * For compactness, this is a 'char' string rather than a 'tchar' string. */ static const char language_names[3149] = "en-US\0ar-SA\0bg-BG\0ca-ES\0zh-CN\0cs-CZ\0da-DK\0de-DE\0el-GR\0en-US\0" "es-ES\0fi-FI\0fr-FR\0he-IL\0hu-HU\0is-IS\0it-IT\0ja-JP\0ko-KR\0nl-NL\0" "nb-NO\0pl-PL\0pt-BR\0rm-CH\0ro-RO\0ru-RU\0hr-HR\0sk-SK\0sq-AL\0sv-SE\0" "th-TH\0tr-TR\0ur-PK\0id-ID\0uk-UA\0be-BY\0sl-SI\0et-EE\0lv-LV\0lt-LT\0" "tg-Cyrl-TJ\0fa-IR\0vi-VN\0hy-AM\0az-Latn-AZ\0eu-ES\0hsb-DE\0mk-MK\0" "st-ZA\0ts-ZA\0tn-ZA\0ve-ZA\0xh-ZA\0zu-ZA\0af-ZA\0ka-GE\0fo-FO\0hi-IN\0" "mt-MT\0se-NO\0ga-IE\0yi-001\0ms-MY\0kk-KZ\0ky-KG\0sw-KE\0tk-TM\0" "uz-Latn-UZ\0tt-RU\0bn-IN\0pa-IN\0gu-IN\0or-IN\0ta-IN\0te-IN\0kn-IN\0" "ml-IN\0as-IN\0mr-IN\0sa-IN\0mn-MN\0bo-CN\0cy-GB\0km-KH\0lo-LA\0my-MM\0" "gl-ES\0kok-IN\0mni-IN\0sd-Arab-PK\0syr-SY\0si-LK\0chr-Cher-US\0" "iu-Latn-CA\0am-ET\0tzm-Latn-DZ\0ks-Arab-IN\0ne-NP\0fy-NL\0ps-AF\0" "fil-PH\0dv-MV\0bin-NG\0ff-Latn-SN\0ha-Latn-NG\0ibb-NG\0yo-NG\0quz-BO\0" "nso-ZA\0ba-RU\0lb-LU\0kl-GL\0ig-NG\0kr-NG\0om-ET\0ti-ER\0gn-PY\0" "haw-US\0la-001\0so-SO\0ii-CN\0pap-029\0arn-CL\0moh-CA\0br-FR\0\0" "ug-CN\0mi-NZ\0oc-FR\0co-FR\0gsw-FR\0sah-RU\0quc-Latn-GT\0rw-RW\0" "wo-SN\0prs-AF\0gd-GB\0ku-Arab-IQ\0en-US\0ar-SA\0bg-BG\0ca-ES\0zh-TW\0" "cs-CZ\0da-DK\0de-DE\0el-GR\0en-US\0es-ES_tradnl\0fi-FI\0fr-FR\0he-IL\0" "hu-HU\0is-IS\0it-IT\0ja-JP\0ko-KR\0nl-NL\0nb-NO\0pl-PL\0pt-BR\0rm-CH\0" "ro-RO\0ru-RU\0hr-HR\0sk-SK\0sq-AL\0sv-SE\0th-TH\0tr-TR\0ur-PK\0id-ID\0" "uk-UA\0be-BY\0sl-SI\0et-EE\0lv-LV\0lt-LT\0tg-Cyrl-TJ\0fa-IR\0vi-VN\0" "hy-AM\0az-Latn-AZ\0eu-ES\0hsb-DE\0mk-MK\0st-ZA\0ts-ZA\0tn-ZA\0ve-ZA\0" "xh-ZA\0zu-ZA\0af-ZA\0ka-GE\0fo-FO\0hi-IN\0mt-MT\0se-NO\0yi-001\0" "ms-MY\0kk-KZ\0ky-KG\0sw-KE\0tk-TM\0uz-Latn-UZ\0tt-RU\0bn-IN\0pa-IN\0" "gu-IN\0or-IN\0ta-IN\0te-IN\0kn-IN\0ml-IN\0as-IN\0mr-IN\0sa-IN\0mn-MN\0" "bo-CN\0cy-GB\0km-KH\0lo-LA\0my-MM\0gl-ES\0kok-IN\0mni-IN\0sd-Deva-IN\0" "syr-SY\0si-LK\0chr-Cher-US\0iu-Cans-CA\0am-ET\0tzm-Arab-MA\0" "ks-Arab-IN\0ne-NP\0fy-NL\0ps-AF\0fil-PH\0dv-MV\0bin-NG\0ff-NG\0" "ha-Latn-NG\0ibb-NG\0yo-NG\0quz-BO\0nso-ZA\0ba-RU\0lb-LU\0kl-GL\0" "ig-NG\0kr-NG\0om-ET\0ti-ET\0gn-PY\0haw-US\0la-001\0so-SO\0ii-CN\0" "pap-029\0arn-CL\0moh-CA\0br-FR\0ug-CN\0mi-NZ\0oc-FR\0co-FR\0gsw-FR\0" "sah-RU\0quc-Latn-GT\0rw-RW\0wo-SN\0prs-AF\0gd-GB\0ku-Arab-IQ\0" "qps-ploc\0qps-ploca\0en-US\0ar-IQ\0ca-ES-valencia\0zh-CN\0de-CH\0" "en-GB\0es-MX\0fr-BE\0it-CH\0nl-BE\0nn-NO\0pt-PT\0ro-MD\0ru-MD\0" "sr-Latn-CS\0sv-FI\0ur-IN\0az-Cyrl-AZ\0dsb-DE\0tn-BW\0se-SE\0ga-IE\0" "ms-BN\0uz-Cyrl-UZ\0bn-BD\0pa-Arab-PK\0ta-LK\0mn-Mong-CN\0sd-Arab-PK\0" "iu-Latn-CA\0tzm-Latn-DZ\0ks-Deva-IN\0ne-IN\0ff-Latn-SN\0quz-EC\0" "ti-ER\0qps-Latn-x-sh\0qps-plocm\0en-US\0ar-EG\0zh-HK\0de-AT\0en-AU\0" "es-ES\0fr-CA\0sr-Cyrl-CS\0se-FI\0mn-Mong-MN\0dz-BT\0quz-PE\0" "ks-Arab-IN\0ar-LY\0zh-SG\0de-LU\0en-CA\0es-GT\0fr-CH\0hr-BA\0smj-NO\0" "tzm-Tfng-MA\0ar-DZ\0zh-MO\0de-LI\0en-NZ\0es-CR\0fr-LU\0bs-Latn-BA\0" "smj-SE\0ar-MA\0en-IE\0es-PA\0fr-MC\0sr-Latn-BA\0sma-NO\0ar-TN\0en-ZA\0" "es-DO\0fr-029\0sr-Cyrl-BA\0sma-SE\0en-US\0ar-OM\0en-JM\0es-VE\0fr-RE\0" "bs-Cyrl-BA\0sms-FI\0en-US\0ar-YE\0en-029\0es-CO\0fr-CD\0sr-Latn-RS\0" "smn-FI\0en-US\0ar-SY\0en-BZ\0es-PE\0fr-SN\0sr-Cyrl-RS\0en-US\0ar-JO\0" "en-TT\0es-AR\0fr-CM\0sr-Latn-ME\0en-US\0ar-LB\0en-ZW\0es-EC\0fr-CI\0" "sr-Cyrl-ME\0en-US\0ar-KW\0en-PH\0es-CL\0fr-ML\0en-US\0ar-AE\0en-ID\0" "es-UY\0fr-MA\0en-US\0ar-BH\0en-HK\0es-PY\0fr-HT\0en-US\0ar-QA\0en-IN\0" "es-BO\0en-US\0en-MY\0es-SV\0en-US\0en-SG\0es-HN\0en-US\0es-NI\0es-PR\0" "es-US\0es-419\0es-CU\0bs-Cyrl-BA\0bs-Latn-BA\0sr-Cyrl-RS\0sr-Latn-RS\0" "smn-FI\0az-Cyrl-AZ\0sms-FI\0zh-CN\0nn-NO\0bs-Latn-BA\0az-Latn-AZ\0" "sma-SE\0uz-Cyrl-UZ\0mn-MN\0iu-Cans-CA\0tzm-Tfng-MA\0zh-HK\0nb-NO\0" "sr-Latn-RS\0tg-Cyrl-TJ\0dsb-DE\0smj-SE\0uz-Latn-UZ\0pa-Arab-PK\0" "mn-Mong-CN\0sd-Arab-PK\0chr-Cher-US\0iu-Latn-CA\0tzm-Latn-DZ\0" "ff-Latn-SN\0ha-Latn-NG\0quc-Latn-GT\0ku-Arab-IQ\0"; /* Translate a Windows language ID to its name. Returns NULL if the ID is not * recognized. */ static const char * language_id_to_name(u16 id) { int l = 0; int r = ARRAY_LEN(language_id_map) - 1; do { int m = (l + r) / 2; if (id < language_id_map[m].id) r = m - 1; else if (id > language_id_map[m].id) l = m + 1; else return &language_names[language_id_map[m].name_start_offset]; } while (l <= r); return NULL; } /* PE binary processor architecture codes (common ones only) */ #define IMAGE_FILE_MACHINE_I386 0x014C #define IMAGE_FILE_MACHINE_ARM 0x01C0 #define IMAGE_FILE_MACHINE_ARMV7 0x01C4 #define IMAGE_FILE_MACHINE_THUMB 0x01C2 #define IMAGE_FILE_MACHINE_IA64 0x0200 #define IMAGE_FILE_MACHINE_AMD64 0x8664 #define IMAGE_FILE_MACHINE_ARM64 0xAA64 /* Windows API processor architecture codes (common ones only) */ #define PROCESSOR_ARCHITECTURE_INTEL 0 #define PROCESSOR_ARCHITECTURE_ARM 5 #define PROCESSOR_ARCHITECTURE_IA64 6 #define PROCESSOR_ARCHITECTURE_AMD64 9 #define PROCESSOR_ARCHITECTURE_ARM64 12 /* Translate a processor architecture code as given in a PE binary to the code * used by the Windows API. Returns -1 if the code is not recognized. */ static int pe_arch_to_windows_arch(unsigned pe_arch) { switch (pe_arch) { case IMAGE_FILE_MACHINE_I386: return PROCESSOR_ARCHITECTURE_INTEL; case IMAGE_FILE_MACHINE_ARM: case IMAGE_FILE_MACHINE_ARMV7: case IMAGE_FILE_MACHINE_THUMB: return PROCESSOR_ARCHITECTURE_ARM; case IMAGE_FILE_MACHINE_IA64: return PROCESSOR_ARCHITECTURE_IA64; case IMAGE_FILE_MACHINE_AMD64: return PROCESSOR_ARCHITECTURE_AMD64; case IMAGE_FILE_MACHINE_ARM64: return PROCESSOR_ARCHITECTURE_ARM64; } return -1; } /* Gather information from kernel32.dll. */ static void set_info_from_kernel32(struct windows_info_ctx *ctx, const void *contents, size_t size) { u32 e_lfanew; const u8 *pe_hdr; unsigned pe_arch; int arch; /* Read the processor architecture from the executable header. */ if (size < 0x40) goto invalid; e_lfanew = le32_to_cpu(*(le32 *)((u8 *)contents + 0x3C)); if (e_lfanew > size || size - e_lfanew < 6 || (e_lfanew & 3)) goto invalid; pe_hdr = (u8 *)contents + e_lfanew; if (*(le32 *)pe_hdr != cpu_to_le32(0x00004550)) /* "PE\0\0" */ goto invalid; pe_arch = le16_to_cpu(*(le16 *)(pe_hdr + 4)); arch = pe_arch_to_windows_arch(pe_arch); if (arch >= 0) { /* Save the processor architecture in the XML document. */ set_number_property(ctx, T("WINDOWS/ARCH"), arch); } else { XML_WARN("Architecture value %x from kernel32.dll " "header not recognized", pe_arch); } return; invalid: XML_WARN("kernel32.dll is not a valid PE binary."); } /* Gather information from the SOFTWARE registry hive. */ static void set_info_from_software_hive(struct windows_info_ctx *ctx, const struct regf *regf) { const tchar *version_key = T("Microsoft\\Windows NT\\CurrentVersion"); s64 major_version = -1; s64 minor_version = -1; tchar *version_string; tchar *build_string; /* Image flags */ copy_registry_string(ctx, regf, version_key, T("EditionID"), T("FLAGS")); /* Image display name */ copy_registry_string(ctx, regf, version_key, T("ProductName"), T("DISPLAYNAME")); /* Image display description */ copy_registry_string(ctx, regf, version_key, T("ProductName"), T("DISPLAYDESCRIPTION")); /* Edition ID */ copy_registry_string(ctx, regf, version_key, T("EditionID"), T("WINDOWS/EDITIONID")); /* Installation type */ copy_registry_string(ctx, regf, version_key, T("InstallationType"), T("WINDOWS/INSTALLATIONTYPE")); /* Product name */ copy_registry_string(ctx, regf, version_key, T("ProductName"), T("WINDOWS/PRODUCTNAME")); /* Major and minor version number */ /* Note: in Windows 10, CurrentVersion was apparently fixed at 6.3. * Instead, the new values CurrentMajorVersionNumber and * CurrentMinorVersionNumber should be used. */ get_number_from_registry(ctx, regf, version_key, T("CurrentMajorVersionNumber"), &major_version); get_number_from_registry(ctx, regf, version_key, T("CurrentMinorVersionNumber"), &minor_version); if (major_version < 0 || minor_version < 0) { if (get_string_from_registry(ctx, regf, version_key, T("CurrentVersion"), &version_string)) { if (2 != tscanf(version_string, T("%"PRIi64".%"PRIi64), &major_version, &minor_version)) { XML_WARN("Unrecognized CurrentVersion: %"TS, version_string); } FREE(version_string); } } if (major_version >= 0) { set_number_property(ctx, T("WINDOWS/VERSION/MAJOR"), major_version); if (minor_version >= 0) { set_number_property(ctx, T("WINDOWS/VERSION/MINOR"), minor_version); } } /* Build number */ /* Note: "CurrentBuild" is marked as obsolete in Windows XP registries * (example value: "1.511.1 () (Obsolete data - do not use)"), and * "CurrentBuildNumber" contains the correct value. But oddly enough, * it is "CurrentBuild" that contains the correct value on *later* * versions of Windows. */ if (get_string_from_registry(ctx, regf, version_key, T("CurrentBuild"), &build_string)) { if (tstrchr(build_string, T('.'))) { FREE(build_string); build_string = NULL; get_string_from_registry(ctx, regf, version_key, T("CurrentBuildNumber"), &build_string); } if (build_string) { set_string_property(ctx, T("WINDOWS/VERSION/BUILD"), build_string); FREE(build_string); } } } /* Gather the default language from the SYSTEM registry hive. */ static void set_default_language(struct windows_info_ctx *ctx, const struct regf *regf) { tchar *string; unsigned language_id; if (!get_string_from_registry(ctx, regf, T("ControlSet001\\Control\\Nls\\Language"), T("InstallLanguage"), &string)) return; if (1 == tscanf(string, T("%x"), &language_id)) { const char *language_name = language_id_to_name(language_id); if (language_name) { size_t len = strlen(language_name); tchar tstr[len + 1]; for (size_t i = 0; i <= len; i++) tstr[i] = language_name[i]; set_string_property(ctx, T("WINDOWS/LANGUAGES/DEFAULT"), tstr); FREE(string); return; } } XML_WARN("Unrecognized InstallLanguage: %"TS, string); FREE(string); } /* Gather information from the SYSTEM registry hive. */ static void set_info_from_system_hive(struct windows_info_ctx *ctx, const struct regf *regf) { const tchar *windows_key = T("ControlSet001\\Control\\Windows"); const tchar *uilanguages_key = T("ControlSet001\\Control\\MUI\\UILanguages"); const tchar *productoptions_key = T("ControlSet001\\Control\\ProductOptions"); s64 spbuild; s64 splevel; tchar **subkeys; /* Service pack build */ if (get_number_from_registry(ctx, regf, windows_key, T("CSDBuildNumber"), &spbuild)) set_number_property(ctx, T("WINDOWS/VERSION/SPBUILD"), spbuild); /* Service pack level */ if (get_number_from_registry(ctx, regf, windows_key, T("CSDVersion"), &splevel)) set_number_property(ctx, T("WINDOWS/VERSION/SPLEVEL"), splevel >> 8); /* Product type */ copy_registry_string(ctx, regf, productoptions_key, T("ProductType"), T("WINDOWS/PRODUCTTYPE")); /* Product suite */ copy_registry_string(ctx, regf, productoptions_key, T("ProductSuite"), T("WINDOWS/PRODUCTSUITE")); /* Hardware abstraction layer */ copy_registry_string(ctx, regf, T("ControlSet001\\Control\\Class\\{4D36E966-E325-11CE-BFC1-08002BE10318}\\0000"), T("MatchingDeviceId"), T("WINDOWS/HAL")); /* Languages */ if (list_subkeys_in_registry(ctx, regf, uilanguages_key, &subkeys)) { tchar property_name[64]; for (tchar **p = subkeys; *p; p++) { tsprintf(property_name, T("WINDOWS/LANGUAGES/LANGUAGE[%zu]"), p - subkeys + 1); set_string_property(ctx, property_name, *p); } hive_free_subkeys_list(subkeys); } /* Default language */ set_default_language(ctx, regf); } /* Load the contents of a file in the currently selected WIM image into memory. */ static void * load_file_contents(struct windows_info_ctx *ctx, const struct wim_dentry *dentry, const char *filename, size_t *size_ret) { const struct blob_descriptor *blob; void *contents; int ret; if (!dentry) { XML_WARN("%s does not exist", filename); return NULL; } blob = inode_get_blob_for_unnamed_data_stream(dentry->d_inode, ctx->wim->blob_table); if (!blob) { XML_WARN("%s has no contents", filename); return NULL; } ret = read_blob_into_alloc_buf(blob, &contents); if (ret) { XML_WARN("Error loading %s (size=%"PRIu64"): %"TS, filename, blob->size, wimlib_get_error_string(ret)); ctx->oom_encountered |= (ret == WIMLIB_ERR_NOMEM && blob->size < 100000000); return NULL; } *size_ret = blob->size; return contents; } /* Load and validate a registry hive file. */ static void * load_hive(struct windows_info_ctx *ctx, const struct wim_dentry *dentry, const char *filename) { void *hive_mem; size_t hive_size; hive_mem = load_file_contents(ctx, dentry, filename, &hive_size); if (hive_mem && !is_registry_valid(ctx, hive_mem, hive_size)) { XML_WARN("%s is not a valid registry hive!", filename); FREE(hive_mem); hive_mem = NULL; } return hive_mem; } /* Set the WINDOWS/SYSTEMROOT property to the name of the directory specified by * 'systemroot'. */ static void set_systemroot_property(struct windows_info_ctx *ctx, const struct wim_dentry *systemroot) { utf16lechar *uname; const tchar *name; size_t name_nbytes; int ret; /* to uppercase ... */ uname = utf16le_dupz(systemroot->d_name, systemroot->d_name_nbytes); if (!uname) { ctx->oom_encountered = true; goto out; } for (size_t i = 0; i < systemroot->d_name_nbytes / 2; i++) uname[i] = cpu_to_le16(upcase[le16_to_cpu(uname[i])]); /* to tstring ... */ ret = utf16le_get_tstr(uname, systemroot->d_name_nbytes, &name, &name_nbytes); if (ret) { ctx->oom_encountered |= (ret == WIMLIB_ERR_NOMEM); XML_WARN("Failed to get systemroot name: %"TS, wimlib_get_error_string(ret)); goto out; } set_string_property(ctx, T("WINDOWS/SYSTEMROOT"), name); utf16le_put_tstr(name); out: FREE(uname); } static int do_set_windows_specific_info(WIMStruct *wim, const struct wim_dentry *systemroot, const struct wim_dentry *kernel32, const struct wim_dentry *software, const struct wim_dentry *system) { void *contents; size_t size; struct windows_info_ctx _ctx = { .wim = wim, .image = wim->current_image, .oom_encountered = false, .debug_enabled = (tgetenv(T("WIMLIB_DEBUG_XML_INFO")) != NULL), }, *ctx = &_ctx; set_systemroot_property(ctx, systemroot); if ((contents = load_file_contents(ctx, kernel32, "kernel32.dll", &size))) { set_info_from_kernel32(ctx, contents, size); FREE(contents); } if ((contents = load_hive(ctx, software, "SOFTWARE"))) { set_info_from_software_hive(ctx, contents); FREE(contents); } if ((contents = load_hive(ctx, system, "SYSTEM"))) { set_info_from_system_hive(ctx, contents); FREE(contents); } if (ctx->oom_encountered) { ERROR("Ran out of memory while setting Windows-specific " "metadata in the WIM file's XML document."); return WIMLIB_ERR_NOMEM; } return 0; } /* Windows */ static const utf16lechar windows_name[] = { cpu_to_le16('W'), cpu_to_le16('i'), cpu_to_le16('n'), cpu_to_le16('d'), cpu_to_le16('o'), cpu_to_le16('w'), cpu_to_le16('s'), }; /* System32 */ static const utf16lechar system32_name[] = { cpu_to_le16('S'), cpu_to_le16('y'), cpu_to_le16('s'), cpu_to_le16('t'), cpu_to_le16('e'), cpu_to_le16('m'), cpu_to_le16('3'), cpu_to_le16('2'), }; /* kernel32.dll */ static const utf16lechar kernel32_name[] = { cpu_to_le16('k'), cpu_to_le16('e'), cpu_to_le16('r'), cpu_to_le16('n'), cpu_to_le16('e'), cpu_to_le16('l'), cpu_to_le16('3'), cpu_to_le16('2'), cpu_to_le16('.'), cpu_to_le16('d'), cpu_to_le16('l'), cpu_to_le16('l'), }; /* config */ static const utf16lechar config_name[] = { cpu_to_le16('c'), cpu_to_le16('o'), cpu_to_le16('n'), cpu_to_le16('f'), cpu_to_le16('i'), cpu_to_le16('g'), }; /* SOFTWARE */ static const utf16lechar software_name[] = { cpu_to_le16('S'), cpu_to_le16('O'), cpu_to_le16('F'), cpu_to_le16('T'), cpu_to_le16('W'), cpu_to_le16('A'), cpu_to_le16('R'), cpu_to_le16('E'), }; /* SYSTEM */ static const utf16lechar system_name[] = { cpu_to_le16('S'), cpu_to_le16('Y'), cpu_to_le16('S'), cpu_to_le16('T'), cpu_to_le16('E'), cpu_to_le16('M'), }; #define GET_CHILD(parent, child_name) \ get_dentry_child_with_utf16le_name(parent, \ child_name, \ sizeof(child_name), \ WIMLIB_CASE_INSENSITIVE) static bool is_default_systemroot(const struct wim_dentry *potential_systemroot) { return !cmp_utf16le_strings(potential_systemroot->d_name, potential_systemroot->d_name_nbytes / 2, windows_name, ARRAY_LEN(windows_name), true); } /* * Set Windows-specific XML information for the currently selected WIM image. * * This process is heavily based on heuristics and hard-coded logic related to * where Windows stores certain types of information. Therefore, it simply * tries to set as much information as possible. If there's a problem, it skips * the affected information and proceeds to the next part. It only returns an * error code if there was a severe problem such as out-of-memory. */ int set_windows_specific_info(WIMStruct *wim) { const struct wim_dentry *root, *potential_systemroot, *best_systemroot = NULL, *best_kernel32 = NULL, *best_software = NULL, *best_system = NULL; int best_score = 0; root = wim_get_current_root_dentry(wim); if (!root) return 0; /* Find the system root. This is usually the toplevel directory * "Windows", but it might be a different toplevel directory. Choose * the directory that contains the greatest number of the files we want: * System32/kernel32.dll, System32/config/SOFTWARE, and * System32/config/SYSTEM. Compare all names case insensitively. */ for_dentry_child(potential_systemroot, root) { const struct wim_dentry *system32, *kernel32, *config, *software = NULL, *system = NULL; int score; if (!dentry_is_directory(potential_systemroot)) continue; system32 = GET_CHILD(potential_systemroot, system32_name); if (!system32) continue; kernel32 = GET_CHILD(system32, kernel32_name); config = GET_CHILD(system32, config_name); if (config) { software = GET_CHILD(config, software_name); system = GET_CHILD(config, system_name); } score = !!kernel32 + !!software + !!system; if (score >= best_score) { /* If there's a tie, prefer the "Windows" directory. */ if (score > best_score || is_default_systemroot(potential_systemroot)) { best_score = score; best_systemroot = potential_systemroot; best_kernel32 = kernel32; best_software = software; best_system = system; } } } if (likely(best_score == 0)) return 0; /* No Windows system root found. */ /* Found the Windows system root. */ return do_set_windows_specific_info(wim, best_systemroot, best_kernel32, best_software, best_system); } wimlib-1.13.1/src/lzms_decompress.c0000644000175000017500000010276713160354225014163 00000000000000/* * lzms_decompress.c * * A decompressor for the LZMS compression format. */ /* * Copyright (C) 2013-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ /* * This is a decompressor for the LZMS compression format used by Microsoft. * This format is not documented, but it is one of the formats supported by the * compression API available in Windows 8, and as of Windows 8 it is one of the * formats that can be used in WIM files. * * This decompressor only implements "raw" decompression, which decompresses a * single LZMS-compressed block. This behavior is the same as that of * Decompress() in the Windows 8 compression API when using a compression handle * created with CreateDecompressor() with the Algorithm parameter specified as * COMPRESS_ALGORITHM_LZMS | COMPRESS_RAW. Presumably, non-raw LZMS data is a * container format from which the locations and sizes (both compressed and * uncompressed) of the constituent blocks can be determined. * * An LZMS-compressed block must be read in 16-bit little endian units from both * directions. One logical bitstream starts at the front of the block and * proceeds forwards. Another logical bitstream starts at the end of the block * and proceeds backwards. Bits read from the forwards bitstream constitute * binary range-encoded data, whereas bits read from the backwards bitstream * constitute Huffman-encoded symbols or verbatim bits. For both bitstreams, * the ordering of the bits within the 16-bit coding units is such that the * first bit is the high-order bit and the last bit is the low-order bit. * * From these two logical bitstreams, an LZMS decompressor can reconstitute the * series of items that make up the LZMS data representation. Each such item * may be a literal byte or a match. Matches may be either traditional LZ77 * matches or "delta" matches, either of which can have its offset encoded * explicitly or encoded via a reference to a recently used (repeat) offset. * * A traditional LZ77 match consists of a length and offset. It asserts that * the sequence of bytes beginning at the current position and extending for the * length is equal to the same-length sequence of bytes at the offset back in * the data buffer. This type of match can be visualized as follows, with the * caveat that the sequences may overlap: * * offset * -------------------- * | | * B[1...len] A[1...len] * * Decoding proceeds as follows: * * do { * *A++ = *B++; * } while (--length); * * On the other hand, a delta match consists of a "span" as well as a length and * offset. A delta match can be visualized as follows, with the caveat that the * various sequences may overlap: * * offset * ----------------------------- * | | * span | span | * ------------- ------------- * | | | | * D[1...len] C[1...len] B[1...len] A[1...len] * * Decoding proceeds as follows: * * do { * *A++ = *B++ + *C++ - *D++; * } while (--length); * * A delta match asserts that the bytewise differences of the A and B sequences * are equal to the bytewise differences of the C and D sequences. The * sequences within each pair are separated by the same number of bytes, the * "span". The inter-pair distance is the "offset". In LZMS, spans are * restricted to powers of 2 between 2**0 and 2**7 inclusively. Offsets are * restricted to multiples of the span. The stored value for the offset is the * "raw offset", which is the real offset divided by the span. * * Delta matches can cover data containing a series of power-of-2 sized integers * that is linearly increasing or decreasing. Another way of thinking about it * is that a delta match can match a longer sequence that is interrupted by a * non-matching byte, provided that the non-matching byte is a continuation of a * linearly changing pattern. Examples of files that may contain data like this * are uncompressed bitmap images, uncompressed digital audio, and Unicode data * tables. To some extent, this match type is a replacement for delta filters * or multimedia filters that are sometimes used in other compression software * (e.g. 'xz --delta --lzma2'). However, on most types of files, delta matches * do not seem to be very useful. * * Both LZ and delta matches may use overlapping sequences. Therefore, they * must be decoded as if only one byte is copied at a time. * * For both LZ and delta matches, any match length in [1, 1073809578] can be * represented. Similarly, any match offset in [1, 1180427428] can be * represented. For delta matches, this range applies to the raw offset, so the * real offset may be larger. * * For LZ matches, up to 3 repeat offsets are allowed, similar to some other * LZ-based formats such as LZX and LZMA. They must updated in an LRU fashion, * except for a quirk: inserting anything to the front of the queue must be * delayed by one LZMS item. The reason for this is presumably that there is * almost no reason to code the same match offset twice in a row, since you * might as well have coded a longer match at that offset. For this same * reason, it also is a requirement that when an offset in the queue is used, * that offset is removed from the queue immediately (and made pending for * front-insertion after the following decoded item), and everything to the * right is shifted left one queue slot. This creates a need for an "overflow" * fourth entry in the queue, even though it is only possible to decode * references to the first 3 entries at any given time. The queue must be * initialized to the offsets {1, 2, 3, 4}. * * Repeat delta matches are handled similarly, but for them the queue contains * (power, raw offset) pairs. This queue must be initialized to * {(0, 1), (0, 2), (0, 3), (0, 4)}. * * Bits from the binary range decoder must be used to disambiguate item types. * The range decoder must hold two state variables: the range, which must * initially be set to 0xffffffff, and the current code, which must initially be * set to the first 32 bits read from the forwards bitstream. The range must be * maintained above 0xffff; when it falls below 0xffff, both the range and code * must be left-shifted by 16 bits and the low 16 bits of the code must be * filled in with the next 16 bits from the forwards bitstream. * * To decode each bit, the binary range decoder requires a probability that is * logically a real number between 0 and 1. Multiplying this probability by the * current range and taking the floor gives the bound between the 0-bit region of * the range and the 1-bit region of the range. However, in LZMS, probabilities * are restricted to values of n/64 where n is an integer is between 1 and 63 * inclusively, so the implementation may use integer operations instead. * Following calculation of the bound, if the current code is in the 0-bit * region, the new range becomes the current code and the decoded bit is 0; * otherwise, the bound must be subtracted from both the range and the code, and * the decoded bit is 1. More information about range coding can be found at * https://en.wikipedia.org/wiki/Range_encoding. Furthermore, note that the * LZMA format also uses range coding and has public domain code available for * it. * * The probability used to range-decode each bit must be taken from a table, of * which one instance must exist for each distinct context, or "binary decision * class", in which a range-decoded bit is needed. At each call of the range * decoder, the appropriate probability must be obtained by indexing the * appropriate probability table with the last 4 (in the context disambiguating * literals from matches), 5 (in the context disambiguating LZ matches from * delta matches), or 6 (in all other contexts) bits recently range-decoded in * that context, ordered such that the most recently decoded bit is the * low-order bit of the index. * * Furthermore, each probability entry itself is variable, as its value must be * maintained as n/64 where n is the number of 0 bits in the most recently * decoded 64 bits with that same entry. This allows the compressed * representation to adapt to the input and use fewer bits to represent the most * likely data; note that LZMA uses a similar scheme. Initially, the most * recently 64 decoded bits for each probability entry are assumed to be * 0x0000000055555555 (high order to low order); therefore, all probabilities * are initially 48/64. During the course of decoding, each probability may be * updated to as low as 0/64 (as a result of reading many consecutive 1 bits * with that entry) or as high as 64/64 (as a result of reading many consecutive * 0 bits with that entry); however, probabilities of 0/64 and 64/64 cannot be * used as-is but rather must be adjusted to 1/64 and 63/64, respectively, * before being used for range decoding. * * Representations of the LZMS items themselves must be read from the backwards * bitstream. For this, there are 5 different Huffman codes used: * * - The literal code, used for decoding literal bytes. Each of the 256 * symbols represents a literal byte. This code must be rebuilt whenever * 1024 symbols have been decoded with it. * * - The LZ offset code, used for decoding the offsets of standard LZ77 * matches. Each symbol represents an offset slot, which corresponds to a * base value and some number of extra bits which must be read and added to * the base value to reconstitute the full offset. The number of symbols in * this code is the number of offset slots needed to represent all possible * offsets in the uncompressed block. This code must be rebuilt whenever * 1024 symbols have been decoded with it. * * - The length code, used for decoding length symbols. Each of the 54 symbols * represents a length slot, which corresponds to a base value and some * number of extra bits which must be read and added to the base value to * reconstitute the full length. This code must be rebuilt whenever 512 * symbols have been decoded with it. * * - The delta offset code, used for decoding the raw offsets of delta matches. * Each symbol corresponds to an offset slot, which corresponds to a base * value and some number of extra bits which must be read and added to the * base value to reconstitute the full raw offset. The number of symbols in * this code is equal to the number of symbols in the LZ offset code. This * code must be rebuilt whenever 1024 symbols have been decoded with it. * * - The delta power code, used for decoding the powers of delta matches. Each * of the 8 symbols corresponds to a power. This code must be rebuilt * whenever 512 symbols have been decoded with it. * * Initially, each Huffman code must be built assuming that each symbol in that * code has frequency 1. Following that, each code must be rebuilt each time a * certain number of symbols, as noted above, has been decoded with it. The * symbol frequencies for a code must be halved after each rebuild of that code; * this makes the codes adapt to the more recent data. * * Like other compression formats such as XPRESS, LZX, and DEFLATE, the LZMS * format requires that all Huffman codes be constructed in canonical form. * This form requires that same-length codewords be lexicographically ordered * the same way as the corresponding symbols and that all shorter codewords * lexicographically precede longer codewords. Such a code can be constructed * directly from codeword lengths. * * Even with the canonical code restriction, the same frequencies can be used to * construct multiple valid Huffman codes. Therefore, the decompressor needs to * construct the right one. Specifically, the LZMS format requires that the * Huffman code be constructed as if the well-known priority queue algorithm is * used and frequency ties are always broken in favor of leaf nodes. * * Codewords in LZMS are guaranteed to not exceed 15 bits. The format otherwise * places no restrictions on codeword length. Therefore, the Huffman code * construction algorithm that a correct LZMS decompressor uses need not * implement length-limited code construction. But if it does (e.g. by virtue * of being shared among multiple compression algorithms), the details of how it * does so are unimportant, provided that the maximum codeword length parameter * is set to at least 15 bits. * * After all LZMS items have been decoded, the data must be postprocessed to * translate absolute address encoded in x86 instructions into their original * relative addresses. * * Details omitted above can be found in the code. Note that in the absence of * an official specification there is no guarantee that this decompressor * handles all possible cases. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib/compress_common.h" #include "wimlib/decompress_common.h" #include "wimlib/decompressor_ops.h" #include "wimlib/error.h" #include "wimlib/lzms_common.h" #include "wimlib/util.h" /* The TABLEBITS values can be changed; they only affect decoding speed. */ #define LZMS_LITERAL_TABLEBITS 10 #define LZMS_LENGTH_TABLEBITS 9 #define LZMS_LZ_OFFSET_TABLEBITS 11 #define LZMS_DELTA_OFFSET_TABLEBITS 11 #define LZMS_DELTA_POWER_TABLEBITS 7 struct lzms_range_decoder { /* The relevant part of the current range. Although the logical range * for range decoding is a very large integer, only a small portion * matters at any given time, and it can be normalized (shifted left) * whenever it gets too small. */ u32 range; /* The current position in the range encoded by the portion of the input * read so far. */ u32 code; /* Pointer to the next little-endian 16-bit integer in the compressed * input data (reading forwards). */ const u8 *next; /* Pointer to the end of the compressed input data. */ const u8 *end; }; typedef u64 bitbuf_t; struct lzms_input_bitstream { /* Holding variable for bits that have been read from the compressed * data. The bit ordering is high to low. */ bitbuf_t bitbuf; /* Number of bits currently held in @bitbuf. */ unsigned bitsleft; /* Pointer to the one past the next little-endian 16-bit integer in the * compressed input data (reading backwards). */ const u8 *next; /* Pointer to the beginning of the compressed input data. */ const u8 *begin; }; #define BITBUF_NBITS (8 * sizeof(bitbuf_t)) /* Bookkeeping information for an adaptive Huffman code */ struct lzms_huffman_rebuild_info { unsigned num_syms_until_rebuild; unsigned num_syms; unsigned rebuild_freq; u32 *codewords; u32 *freqs; u16 *decode_table; unsigned table_bits; }; struct lzms_decompressor { /* 'last_target_usages' is in union with everything else because it is * only used for postprocessing. */ union { struct { struct lzms_probabilites probs; DECODE_TABLE(literal_decode_table, LZMS_NUM_LITERAL_SYMS, LZMS_LITERAL_TABLEBITS, LZMS_MAX_CODEWORD_LENGTH); u32 literal_freqs[LZMS_NUM_LITERAL_SYMS]; struct lzms_huffman_rebuild_info literal_rebuild_info; DECODE_TABLE(lz_offset_decode_table, LZMS_MAX_NUM_OFFSET_SYMS, LZMS_LZ_OFFSET_TABLEBITS, LZMS_MAX_CODEWORD_LENGTH); u32 lz_offset_freqs[LZMS_MAX_NUM_OFFSET_SYMS]; struct lzms_huffman_rebuild_info lz_offset_rebuild_info; DECODE_TABLE(length_decode_table, LZMS_NUM_LENGTH_SYMS, LZMS_LENGTH_TABLEBITS, LZMS_MAX_CODEWORD_LENGTH); u32 length_freqs[LZMS_NUM_LENGTH_SYMS]; struct lzms_huffman_rebuild_info length_rebuild_info; DECODE_TABLE(delta_offset_decode_table, LZMS_MAX_NUM_OFFSET_SYMS, LZMS_DELTA_OFFSET_TABLEBITS, LZMS_MAX_CODEWORD_LENGTH); u32 delta_offset_freqs[LZMS_MAX_NUM_OFFSET_SYMS]; struct lzms_huffman_rebuild_info delta_offset_rebuild_info; DECODE_TABLE(delta_power_decode_table, LZMS_NUM_DELTA_POWER_SYMS, LZMS_DELTA_POWER_TABLEBITS, LZMS_MAX_CODEWORD_LENGTH); u32 delta_power_freqs[LZMS_NUM_DELTA_POWER_SYMS]; struct lzms_huffman_rebuild_info delta_power_rebuild_info; /* Temporary space for lzms_build_huffman_code() */ union { u32 codewords[LZMS_MAX_NUM_SYMS]; DECODE_TABLE_WORKING_SPACE(working_space, LZMS_MAX_NUM_SYMS, LZMS_MAX_CODEWORD_LENGTH); }; }; // struct s32 last_target_usages[65536]; }; // union }; /* Initialize the input bitstream @is to read backwards from the compressed data * buffer @in that is @count bytes long. */ static void lzms_input_bitstream_init(struct lzms_input_bitstream *is, const u8 *in, size_t count) { is->bitbuf = 0; is->bitsleft = 0; is->next = in + count; is->begin = in; } /* Ensure that at least @num_bits bits are in the bitbuffer variable. * @num_bits cannot be more than 32. */ static forceinline void lzms_ensure_bits(struct lzms_input_bitstream *is, unsigned num_bits) { unsigned avail; if (is->bitsleft >= num_bits) return; avail = BITBUF_NBITS - is->bitsleft; if (UNALIGNED_ACCESS_IS_FAST && CPU_IS_LITTLE_ENDIAN && WORDBYTES == 8 && likely(is->next - is->begin >= 8)) { is->next -= (avail & ~15) >> 3; is->bitbuf |= load_u64_unaligned(is->next) << (avail & 15); is->bitsleft += avail & ~15; } else { if (likely(is->next != is->begin)) { is->next -= sizeof(le16); is->bitbuf |= (bitbuf_t)get_unaligned_le16(is->next) << (avail - 16); } if (likely(is->next != is->begin)) { is->next -= sizeof(le16); is->bitbuf |= (bitbuf_t)get_unaligned_le16(is->next) << (avail - 32); } is->bitsleft += 32; } } /* Get @num_bits bits from the bitbuffer variable. */ static forceinline bitbuf_t lzms_peek_bits(struct lzms_input_bitstream *is, unsigned num_bits) { return (is->bitbuf >> 1) >> (BITBUF_NBITS - num_bits - 1); } /* Remove @num_bits bits from the bitbuffer variable. */ static forceinline void lzms_remove_bits(struct lzms_input_bitstream *is, unsigned num_bits) { is->bitbuf <<= num_bits; is->bitsleft -= num_bits; } /* Remove and return @num_bits bits from the bitbuffer variable. */ static forceinline bitbuf_t lzms_pop_bits(struct lzms_input_bitstream *is, unsigned num_bits) { bitbuf_t bits = lzms_peek_bits(is, num_bits); lzms_remove_bits(is, num_bits); return bits; } /* Read @num_bits bits from the input bitstream. */ static forceinline bitbuf_t lzms_read_bits(struct lzms_input_bitstream *is, unsigned num_bits) { lzms_ensure_bits(is, num_bits); return lzms_pop_bits(is, num_bits); } /* Initialize the range decoder @rd to read forwards from the compressed data * buffer @in that is @count bytes long. */ static void lzms_range_decoder_init(struct lzms_range_decoder *rd, const u8 *in, size_t count) { rd->range = 0xffffffff; rd->code = ((u32)get_unaligned_le16(in) << 16) | get_unaligned_le16(in + 2); rd->next = in + 4; rd->end = in + count; } /* * Decode a bit using the range coder. The current state specifies the * probability entry to use. The state and probability entry will be updated * based on the decoded bit. */ static forceinline int lzms_decode_bit(struct lzms_range_decoder *rd, u32 *state_p, u32 num_states, struct lzms_probability_entry *probs) { struct lzms_probability_entry *prob_entry; u32 prob; u32 bound; /* Load the probability entry corresponding to the current state. */ prob_entry = &probs[*state_p]; /* Update the state early. We'll still need to OR the state with 1 * later if the decoded bit is a 1. */ *state_p = (*state_p << 1) & (num_states - 1); /* Get the probability (out of LZMS_PROBABILITY_DENOMINATOR) that the * next bit is 0. */ prob = lzms_get_probability(prob_entry); /* Normalize if needed. */ if (!(rd->range & 0xFFFF0000)) { rd->range <<= 16; rd->code <<= 16; if (likely(rd->next != rd->end)) { rd->code |= get_unaligned_le16(rd->next); rd->next += sizeof(le16); } } /* Based on the probability, calculate the bound between the 0-bit * region and the 1-bit region of the range. */ bound = (rd->range >> LZMS_PROBABILITY_BITS) * prob; if (rd->code < bound) { /* Current code is in the 0-bit region of the range. */ rd->range = bound; /* Update the state and probability entry based on the decoded bit. */ lzms_update_probability_entry(prob_entry, 0); return 0; } else { /* Current code is in the 1-bit region of the range. */ rd->range -= bound; rd->code -= bound; /* Update the state and probability entry based on the decoded bit. */ lzms_update_probability_entry(prob_entry, 1); *state_p |= 1; return 1; } } static void lzms_build_huffman_code(struct lzms_huffman_rebuild_info *rebuild_info) { make_canonical_huffman_code(rebuild_info->num_syms, LZMS_MAX_CODEWORD_LENGTH, rebuild_info->freqs, (u8 *)rebuild_info->decode_table, rebuild_info->codewords); make_huffman_decode_table(rebuild_info->decode_table, rebuild_info->num_syms, rebuild_info->table_bits, (u8 *)rebuild_info->decode_table, LZMS_MAX_CODEWORD_LENGTH, (u16 *)rebuild_info->codewords); rebuild_info->num_syms_until_rebuild = rebuild_info->rebuild_freq; } static void lzms_init_huffman_code(struct lzms_huffman_rebuild_info *rebuild_info, unsigned num_syms, unsigned rebuild_freq, u32 *codewords, u32 *freqs, u16 *decode_table, unsigned table_bits) { rebuild_info->num_syms = num_syms; rebuild_info->rebuild_freq = rebuild_freq; rebuild_info->codewords = codewords; rebuild_info->freqs = freqs; rebuild_info->decode_table = decode_table; rebuild_info->table_bits = table_bits; lzms_init_symbol_frequencies(freqs, num_syms); lzms_build_huffman_code(rebuild_info); } static void lzms_init_huffman_codes(struct lzms_decompressor *d, unsigned num_offset_slots) { lzms_init_huffman_code(&d->literal_rebuild_info, LZMS_NUM_LITERAL_SYMS, LZMS_LITERAL_CODE_REBUILD_FREQ, d->codewords, d->literal_freqs, d->literal_decode_table, LZMS_LITERAL_TABLEBITS); lzms_init_huffman_code(&d->lz_offset_rebuild_info, num_offset_slots, LZMS_LZ_OFFSET_CODE_REBUILD_FREQ, d->codewords, d->lz_offset_freqs, d->lz_offset_decode_table, LZMS_LZ_OFFSET_TABLEBITS); lzms_init_huffman_code(&d->length_rebuild_info, LZMS_NUM_LENGTH_SYMS, LZMS_LENGTH_CODE_REBUILD_FREQ, d->codewords, d->length_freqs, d->length_decode_table, LZMS_LENGTH_TABLEBITS); lzms_init_huffman_code(&d->delta_offset_rebuild_info, num_offset_slots, LZMS_DELTA_OFFSET_CODE_REBUILD_FREQ, d->codewords, d->delta_offset_freqs, d->delta_offset_decode_table, LZMS_DELTA_OFFSET_TABLEBITS); lzms_init_huffman_code(&d->delta_power_rebuild_info, LZMS_NUM_DELTA_POWER_SYMS, LZMS_DELTA_POWER_CODE_REBUILD_FREQ, d->codewords, d->delta_power_freqs, d->delta_power_decode_table, LZMS_DELTA_POWER_TABLEBITS); } static noinline void lzms_rebuild_huffman_code(struct lzms_huffman_rebuild_info *rebuild_info) { lzms_build_huffman_code(rebuild_info); lzms_dilute_symbol_frequencies(rebuild_info->freqs, rebuild_info->num_syms); } /* XXX: mostly copied from read_huffsym() in decompress_common.h because LZMS * needs its own bitstream */ static forceinline unsigned lzms_decode_huffman_symbol(struct lzms_input_bitstream *is, u16 decode_table[], unsigned table_bits, u32 freqs[], struct lzms_huffman_rebuild_info *rebuild_info) { unsigned entry; unsigned symbol; unsigned length; lzms_ensure_bits(is, LZMS_MAX_CODEWORD_LENGTH); entry = decode_table[lzms_peek_bits(is, table_bits)]; symbol = entry >> DECODE_TABLE_SYMBOL_SHIFT; length = entry & DECODE_TABLE_LENGTH_MASK; if (entry >= (1U << (table_bits + DECODE_TABLE_SYMBOL_SHIFT))) { lzms_remove_bits(is, table_bits); entry = decode_table[symbol + lzms_peek_bits(is, length)]; symbol = entry >> DECODE_TABLE_SYMBOL_SHIFT; length = entry & DECODE_TABLE_LENGTH_MASK; } lzms_remove_bits(is, length); freqs[symbol]++; if (--rebuild_info->num_syms_until_rebuild == 0) lzms_rebuild_huffman_code(rebuild_info); return symbol; } static forceinline unsigned lzms_decode_literal(struct lzms_decompressor *d, struct lzms_input_bitstream *is) { return lzms_decode_huffman_symbol(is, d->literal_decode_table, LZMS_LITERAL_TABLEBITS, d->literal_freqs, &d->literal_rebuild_info); } static forceinline u32 lzms_decode_lz_offset(struct lzms_decompressor *d, struct lzms_input_bitstream *is) { unsigned slot = lzms_decode_huffman_symbol(is, d->lz_offset_decode_table, LZMS_LZ_OFFSET_TABLEBITS, d->lz_offset_freqs, &d->lz_offset_rebuild_info); return lzms_offset_slot_base[slot] + lzms_read_bits(is, lzms_extra_offset_bits[slot]); } static forceinline u32 lzms_decode_length(struct lzms_decompressor *d, struct lzms_input_bitstream *is) { unsigned slot = lzms_decode_huffman_symbol(is, d->length_decode_table, LZMS_LENGTH_TABLEBITS, d->length_freqs, &d->length_rebuild_info); u32 length = lzms_length_slot_base[slot]; unsigned num_extra_bits = lzms_extra_length_bits[slot]; /* Usually most lengths are short and have no extra bits. */ if (num_extra_bits) length += lzms_read_bits(is, num_extra_bits); return length; } static forceinline u32 lzms_decode_delta_offset(struct lzms_decompressor *d, struct lzms_input_bitstream *is) { unsigned slot = lzms_decode_huffman_symbol(is, d->delta_offset_decode_table, LZMS_DELTA_OFFSET_TABLEBITS, d->delta_offset_freqs, &d->delta_offset_rebuild_info); return lzms_offset_slot_base[slot] + lzms_read_bits(is, lzms_extra_offset_bits[slot]); } static forceinline unsigned lzms_decode_delta_power(struct lzms_decompressor *d, struct lzms_input_bitstream *is) { return lzms_decode_huffman_symbol(is, d->delta_power_decode_table, LZMS_DELTA_POWER_TABLEBITS, d->delta_power_freqs, &d->delta_power_rebuild_info); } static int lzms_create_decompressor(size_t max_bufsize, void **d_ret) { struct lzms_decompressor *d; if (max_bufsize > LZMS_MAX_BUFFER_SIZE) return WIMLIB_ERR_INVALID_PARAM; d = ALIGNED_MALLOC(sizeof(struct lzms_decompressor), DECODE_TABLE_ALIGNMENT); if (!d) return WIMLIB_ERR_NOMEM; *d_ret = d; return 0; } /* * Decompress @in_nbytes bytes of LZMS-compressed data at @in and write the * uncompressed data, which had original size @out_nbytes, to @out. Return 0 if * successful or -1 if the compressed data is invalid. */ static int lzms_decompress(const void * const restrict in, const size_t in_nbytes, void * const restrict out, const size_t out_nbytes, void * const restrict _d) { struct lzms_decompressor *d = _d; u8 *out_next = out; u8 * const out_end = out + out_nbytes; struct lzms_range_decoder rd; struct lzms_input_bitstream is; /* LRU queues for match sources */ u32 recent_lz_offsets[LZMS_NUM_LZ_REPS + 1]; u64 recent_delta_pairs[LZMS_NUM_DELTA_REPS + 1]; /* Previous item type: 0 = literal, 1 = LZ match, 2 = delta match. * This is used to handle delayed updates of the LRU queues. Instead of * actually delaying the updates, we can check when decoding each rep * match whether a delayed update needs to be taken into account, and if * so get the match source from slot 'rep_idx + 1' instead of from slot * 'rep_idx'. */ unsigned prev_item_type = 0; /* States and probability entries for item type disambiguation */ u32 main_state = 0; u32 match_state = 0; u32 lz_state = 0; u32 delta_state = 0; u32 lz_rep_states[LZMS_NUM_LZ_REP_DECISIONS] = {}; u32 delta_rep_states[LZMS_NUM_DELTA_REP_DECISIONS] = {}; /* * Requirements on the compressed data: * * 1. LZMS-compressed data is a series of 16-bit integers, so the * compressed data buffer cannot take up an odd number of bytes. * 2. There must be at least 4 bytes of compressed data, since otherwise * we cannot even initialize the range decoder. */ if ((in_nbytes & 1) || (in_nbytes < 4)) return -1; lzms_range_decoder_init(&rd, in, in_nbytes); lzms_input_bitstream_init(&is, in, in_nbytes); lzms_init_probabilities(&d->probs); lzms_init_huffman_codes(d, lzms_get_num_offset_slots(out_nbytes)); for (int i = 0; i < LZMS_NUM_LZ_REPS + 1; i++) recent_lz_offsets[i] = i + 1; for (int i = 0; i < LZMS_NUM_DELTA_REPS + 1; i++) recent_delta_pairs[i] = i + 1; /* Main decode loop */ while (out_next != out_end) { if (!lzms_decode_bit(&rd, &main_state, LZMS_NUM_MAIN_PROBS, d->probs.main)) { /* Literal */ *out_next++ = lzms_decode_literal(d, &is); prev_item_type = 0; } else if (!lzms_decode_bit(&rd, &match_state, LZMS_NUM_MATCH_PROBS, d->probs.match)) { /* LZ match */ u32 offset; u32 length; STATIC_ASSERT(LZMS_NUM_LZ_REPS == 3); if (!lzms_decode_bit(&rd, &lz_state, LZMS_NUM_LZ_PROBS, d->probs.lz)) { /* Explicit offset */ offset = lzms_decode_lz_offset(d, &is); recent_lz_offsets[3] = recent_lz_offsets[2]; recent_lz_offsets[2] = recent_lz_offsets[1]; recent_lz_offsets[1] = recent_lz_offsets[0]; } else { /* Repeat offset */ if (!lzms_decode_bit(&rd, &lz_rep_states[0], LZMS_NUM_LZ_REP_PROBS, d->probs.lz_rep[0])) { offset = recent_lz_offsets[0 + (prev_item_type & 1)]; recent_lz_offsets[0 + (prev_item_type & 1)] = recent_lz_offsets[0]; } else if (!lzms_decode_bit(&rd, &lz_rep_states[1], LZMS_NUM_LZ_REP_PROBS, d->probs.lz_rep[1])) { offset = recent_lz_offsets[1 + (prev_item_type & 1)]; recent_lz_offsets[1 + (prev_item_type & 1)] = recent_lz_offsets[1]; recent_lz_offsets[1] = recent_lz_offsets[0]; } else { offset = recent_lz_offsets[2 + (prev_item_type & 1)]; recent_lz_offsets[2 + (prev_item_type & 1)] = recent_lz_offsets[2]; recent_lz_offsets[2] = recent_lz_offsets[1]; recent_lz_offsets[1] = recent_lz_offsets[0]; } } recent_lz_offsets[0] = offset; prev_item_type = 1; length = lzms_decode_length(d, &is); if (unlikely(lz_copy(length, offset, out, out_next, out_end, LZMS_MIN_MATCH_LENGTH))) return -1; out_next += length; } else { /* Delta match */ /* (See beginning of file for more information.) */ u32 power; u32 raw_offset; u32 span; u32 offset; const u8 *matchptr; u32 length; u64 pair; STATIC_ASSERT(LZMS_NUM_DELTA_REPS == 3); if (!lzms_decode_bit(&rd, &delta_state, LZMS_NUM_DELTA_PROBS, d->probs.delta)) { /* Explicit offset */ power = lzms_decode_delta_power(d, &is); raw_offset = lzms_decode_delta_offset(d, &is); pair = ((u64)power << 32) | raw_offset; recent_delta_pairs[3] = recent_delta_pairs[2]; recent_delta_pairs[2] = recent_delta_pairs[1]; recent_delta_pairs[1] = recent_delta_pairs[0]; } else { if (!lzms_decode_bit(&rd, &delta_rep_states[0], LZMS_NUM_DELTA_REP_PROBS, d->probs.delta_rep[0])) { pair = recent_delta_pairs[0 + (prev_item_type >> 1)]; recent_delta_pairs[0 + (prev_item_type >> 1)] = recent_delta_pairs[0]; } else if (!lzms_decode_bit(&rd, &delta_rep_states[1], LZMS_NUM_DELTA_REP_PROBS, d->probs.delta_rep[1])) { pair = recent_delta_pairs[1 + (prev_item_type >> 1)]; recent_delta_pairs[1 + (prev_item_type >> 1)] = recent_delta_pairs[1]; recent_delta_pairs[1] = recent_delta_pairs[0]; } else { pair = recent_delta_pairs[2 + (prev_item_type >> 1)]; recent_delta_pairs[2 + (prev_item_type >> 1)] = recent_delta_pairs[2]; recent_delta_pairs[2] = recent_delta_pairs[1]; recent_delta_pairs[1] = recent_delta_pairs[0]; } power = pair >> 32; raw_offset = (u32)pair; } recent_delta_pairs[0] = pair; prev_item_type = 2; length = lzms_decode_length(d, &is); span = (u32)1 << power; offset = raw_offset << power; /* raw_offset<> power != raw_offset)) return -1; /* offset+span overflows? */ if (unlikely(offset + span < offset)) return -1; /* buffer underrun? */ if (unlikely(offset + span > out_next - (u8 *)out)) return -1; /* buffer overrun? */ if (unlikely(length > out_end - out_next)) return -1; matchptr = out_next - offset; do { *out_next = *matchptr + *(out_next - span) - *(matchptr - span); out_next++; matchptr++; } while (--length); } } lzms_x86_filter(out, out_nbytes, d->last_target_usages, true); return 0; } static void lzms_free_decompressor(void *_d) { struct lzms_decompressor *d = _d; ALIGNED_FREE(d); } const struct decompressor_ops lzms_decompressor_ops = { .create_decompressor = lzms_create_decompressor, .decompress = lzms_decompress, .free_decompressor = lzms_free_decompressor, }; wimlib-1.13.1/src/x86_cpu_features.c0000644000175000017500000001027313160354225014132 00000000000000/* * x86_cpu_features.c - feature detection for x86 processors * * The following copying information applies to this specific source code file: * * Written in 2015 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib/x86_cpu_features.h" #if defined(__i386__) || defined(__x86_64__) #define DEBUG 0 #if DEBUG # include #endif u32 _x86_cpu_features = 0; /* With old GCC versions we have to manually save and restore the x86_32 PIC * register (ebx). See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47602 */ #if defined(__i386__) && defined(__PIC__) # define EBX_CONSTRAINT "=r" #else # define EBX_CONSTRAINT "=b" #endif /* Execute the CPUID instruction. */ static inline void cpuid(u32 leaf, u32 subleaf, u32 *a, u32 *b, u32 *c, u32 *d) { __asm__(".ifnc %%ebx, %1; mov %%ebx, %1; .endif\n" "cpuid \n" ".ifnc %%ebx, %1; xchg %%ebx, %1; .endif\n" : "=a" (*a), EBX_CONSTRAINT (*b), "=c" (*c), "=d" (*d) : "a" (leaf), "c" (subleaf)); } /* Read an extended control register. */ static inline u64 read_xcr(u32 index) { u32 edx, eax; /* Execute the "xgetbv" instruction. Old versions of binutils do not * recognize this instruction, so list the raw bytes instead. */ __asm__ (".byte 0x0f, 0x01, 0xd0" : "=d" (edx), "=a" (eax) : "c" (index)); return ((u64)edx << 32) | eax; } #define IS_SET(reg, bit) ((reg) & ((u32)1 << (bit))) /* Initialize _x86_cpu_features with bits for interesting processor features. */ void x86_setup_cpu_features(void) { u32 features = 0; u32 dummy1, dummy2, dummy3, dummy4; u32 max_function; u32 features_1, features_2, features_3, features_4; bool os_saves_ymm_regs = false; /* Get maximum supported function */ cpuid(0, 0, &max_function, &dummy2, &dummy3, &dummy4); if (max_function < 1) goto out; /* Standard feature flags */ cpuid(1, 0, &dummy1, &dummy2, &features_2, &features_1); if (IS_SET(features_1, 25)) features |= X86_CPU_FEATURE_SSE; if (IS_SET(features_1, 26)) features |= X86_CPU_FEATURE_SSE2; if (IS_SET(features_2, 0)) features |= X86_CPU_FEATURE_SSE3; if (IS_SET(features_2, 9)) features |= X86_CPU_FEATURE_SSSE3; if (IS_SET(features_2, 19)) features |= X86_CPU_FEATURE_SSE4_1; if (IS_SET(features_2, 20)) features |= X86_CPU_FEATURE_SSE4_2; if (IS_SET(features_2, 27)) /* OSXSAVE set? */ if ((read_xcr(0) & 0x6) == 0x6) os_saves_ymm_regs = true; if (os_saves_ymm_regs && IS_SET(features_2, 28)) features |= X86_CPU_FEATURE_AVX; if (max_function < 7) goto out; /* Extended feature flags */ cpuid(7, 0, &dummy1, &features_3, &features_4, &dummy4); if (IS_SET(features_3, 3)) features |= X86_CPU_FEATURE_BMI; if (os_saves_ymm_regs && IS_SET(features_3, 5)) features |= X86_CPU_FEATURE_AVX2; if (IS_SET(features_3, 8)) features |= X86_CPU_FEATURE_BMI2; out: #if DEBUG printf("Detected x86 CPU features: "); if (features & X86_CPU_FEATURE_SSE) printf("SSE "); if (features & X86_CPU_FEATURE_SSE2) printf("SSE2 "); if (features & X86_CPU_FEATURE_SSE3) printf("SSE3 "); if (features & X86_CPU_FEATURE_SSSE3) printf("SSSE3 "); if (features & X86_CPU_FEATURE_SSE4_1) printf("SSE4.1 "); if (features & X86_CPU_FEATURE_SSE4_2) printf("SSE4.2 "); if (features & X86_CPU_FEATURE_BMI) printf("BMI "); if (features & X86_CPU_FEATURE_AVX) printf("AVX "); if (features & X86_CPU_FEATURE_BMI2) printf("BMI2 "); if (features & X86_CPU_FEATURE_AVX2) printf("AVX2 "); printf("\n"); #endif /* DEBUG */ _x86_cpu_features = features | X86_CPU_FEATURES_KNOWN; } #endif /* __i386__ || __x86_64__ */ wimlib-1.13.1/src/add_image.c0000644000175000017500000001125513160354224012632 00000000000000/* * add_image.c - Add an image to a WIMStruct. */ /* * Copyright (C) 2012-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib.h" #include "wimlib/blob_table.h" #include "wimlib/error.h" #include "wimlib/metadata.h" #include "wimlib/security.h" #include "wimlib/xml.h" /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_add_empty_image(WIMStruct *wim, const tchar *name, int *new_idx_ret) { struct wim_image_metadata *imd; int ret; if (wimlib_image_name_in_use(wim, name)) { ERROR("There is already an image named \"%"TS"\" in the WIM!", name); return WIMLIB_ERR_IMAGE_NAME_COLLISION; } imd = new_empty_image_metadata(); if (!imd) return WIMLIB_ERR_NOMEM; ret = append_image_metadata(wim, imd); if (ret) goto err_put_imd; ret = xml_add_image(wim->xml_info, name); if (ret) goto err_undo_append; if (new_idx_ret) *new_idx_ret = wim->hdr.image_count; return 0; err_undo_append: wim->hdr.image_count--; err_put_imd: put_image_metadata(imd); return ret; } /* Translate the 'struct wimlib_capture_source's passed to * wimlib_add_image_multisource() into 'struct wimlib_update_command's for * wimlib_update_image(). */ static struct wimlib_update_command * capture_sources_to_add_cmds(const struct wimlib_capture_source *sources, size_t num_sources, int add_flags, const tchar *config_file) { struct wimlib_update_command *add_cmds; add_cmds = CALLOC(num_sources, sizeof(add_cmds[0])); if (!add_cmds) return NULL; /* WIMLIB_ADD_FLAG_BOOT is handled by wimlib_add_image_multisource(), * not wimlib_update_image(), so mask it out. * * However, WIMLIB_ADD_FLAG_WIMBOOT is handled by both. */ add_flags &= ~WIMLIB_ADD_FLAG_BOOT; for (size_t i = 0; i < num_sources; i++) { add_cmds[i].op = WIMLIB_UPDATE_OP_ADD; add_cmds[i].add.fs_source_path = sources[i].fs_source_path; add_cmds[i].add.wim_target_path = sources[i].wim_target_path; add_cmds[i].add.add_flags = add_flags; add_cmds[i].add.config_file = (tchar *)config_file; } return add_cmds; } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_add_image_multisource(WIMStruct *wim, const struct wimlib_capture_source *sources, size_t num_sources, const tchar *name, const tchar *config_file, int add_flags) { int ret; struct wimlib_update_command *add_cmds; /* Make sure no reserved fields are set. */ for (size_t i = 0; i < num_sources; i++) if (sources[i].reserved != 0) return WIMLIB_ERR_INVALID_PARAM; /* Add the new image (initially empty). */ ret = wimlib_add_empty_image(wim, name, NULL); if (ret) return ret; /* Translate the "capture sources" into generic update commands. */ ret = WIMLIB_ERR_NOMEM; add_cmds = capture_sources_to_add_cmds(sources, num_sources, add_flags, config_file); if (!add_cmds) goto out_delete_image; /* Delegate the work to wimlib_update_image(). */ ret = wimlib_update_image(wim, wim->hdr.image_count, add_cmds, num_sources, 0); FREE(add_cmds); if (ret) goto out_delete_image; /* If requested, mark the new image as WIMBoot-compatible. */ if (add_flags & WIMLIB_ADD_FLAG_WIMBOOT) { ret = xml_set_wimboot(wim->xml_info, wim->hdr.image_count); if (ret) goto out_delete_image; } /* If requested, set this image as the WIM's bootable image. */ if (add_flags & WIMLIB_ADD_FLAG_BOOT) wim->hdr.boot_idx = wim->hdr.image_count; return 0; out_delete_image: /* Unsuccessful; rollback by removing the new image. */ delete_wim_image(wim, wim->hdr.image_count); return ret; } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_add_image(WIMStruct *wim, const tchar *source, const tchar *name, const tchar *config_file, int add_flags) { /* Use the more general wimlib_add_image_multisource(). */ const struct wimlib_capture_source capture_src = { .fs_source_path = (tchar *)source, .wim_target_path = WIMLIB_WIM_ROOT_PATH, .reserved = 0, }; return wimlib_add_image_multisource(wim, &capture_src, 1, name, config_file, add_flags); } wimlib-1.13.1/src/export_image.c0000644000175000017500000002017013160354224013417 00000000000000/* * export_image.c */ /* * Copyright (C) 2012-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib.h" #include "wimlib/blob_table.h" #include "wimlib/error.h" #include "wimlib/inode.h" #include "wimlib/metadata.h" #include "wimlib/xml.h" static int blob_set_not_exported(struct blob_descriptor *blob, void *_ignore) { blob->out_refcnt = 0; blob->was_exported = 0; return 0; } static int blob_rollback_export(struct blob_descriptor *blob, void *_blob_table) { struct blob_table *blob_table = _blob_table; blob->refcnt -= blob->out_refcnt; if (blob->was_exported) { blob_table_unlink(blob_table, blob); free_blob_descriptor(blob); } return 0; } static int inode_export_blobs(struct wim_inode *inode, struct blob_table *src_blob_table, struct blob_table *dest_blob_table, bool gift) { unsigned i; const u8 *hash; struct blob_descriptor *src_blob, *dest_blob; for (i = 0; i < inode->i_num_streams; i++) { /* Retrieve SHA-1 message digest of blob to export. */ hash = stream_hash(&inode->i_streams[i]); if (is_zero_hash(hash)) /* Empty stream? */ continue; /* Search for the blob (via SHA-1 message digest) in the * destination WIM. */ dest_blob = lookup_blob(dest_blob_table, hash); if (!dest_blob) { /* Blob not yet present in destination WIM. Search for * it in the source WIM, then export it into the * destination WIM. */ src_blob = stream_blob(&inode->i_streams[i], src_blob_table); if (!src_blob) return blob_not_found_error(inode, hash); if (gift) { dest_blob = src_blob; blob_table_unlink(src_blob_table, src_blob); } else { dest_blob = clone_blob_descriptor(src_blob); if (!dest_blob) return WIMLIB_ERR_NOMEM; } dest_blob->refcnt = 0; dest_blob->out_refcnt = 0; dest_blob->was_exported = 1; blob_table_insert(dest_blob_table, dest_blob); } /* Blob is present in destination WIM (either pre-existing, * already exported, or just exported above). Increment its * reference count appropriately. Note: we use 'refcnt' for * the raw reference count, but 'out_refcnt' for references * arising just from the export operation; this is used to roll * back a failed export if needed. */ dest_blob->refcnt += inode->i_nlink; dest_blob->out_refcnt += inode->i_nlink; } return 0; } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim, int src_image, WIMStruct *dest_wim, const tchar *dest_name, const tchar *dest_description, int export_flags) { int ret; int start_src_image; int end_src_image; int orig_dest_image_count; int image; bool all_images = (src_image == WIMLIB_ALL_IMAGES); /* Check for sane parameters. */ if (export_flags & ~(WIMLIB_EXPORT_FLAG_BOOT | WIMLIB_EXPORT_FLAG_NO_NAMES | WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS | WIMLIB_EXPORT_FLAG_GIFT | WIMLIB_EXPORT_FLAG_WIMBOOT)) return WIMLIB_ERR_INVALID_PARAM; if (!src_wim || !dest_wim) return WIMLIB_ERR_INVALID_PARAM; if (!wim_has_metadata(src_wim) || !wim_has_metadata(dest_wim)) return WIMLIB_ERR_METADATA_NOT_FOUND; if (all_images) { /* Multi-image export. */ if ((!(export_flags & WIMLIB_EXPORT_FLAG_NO_NAMES) && dest_name) || (!(export_flags & WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS) && dest_description)) { ERROR("Image name and description must be " "left NULL for multi-image export"); return WIMLIB_ERR_INVALID_PARAM; } start_src_image = 1; end_src_image = src_wim->hdr.image_count; } else { start_src_image = src_image; end_src_image = src_image; } orig_dest_image_count = dest_wim->hdr.image_count; /* We don't yet support having a single WIMStruct contain duplicate * 'image_metadata' structures, so we must forbid this from happening. * A duplication is possible if 'src_wim == dest_wim', if the same image * is exported to the same destination WIMStruct multiple times, or if * an image is exported in an A => B => A manner. */ for (src_image = start_src_image; src_image <= end_src_image; src_image++) { const struct wim_image_metadata *src_imd = src_wim->image_metadata[src_image - 1]; for (int i = 0; i < dest_wim->hdr.image_count; i++) if (dest_wim->image_metadata[i] == src_imd) return WIMLIB_ERR_DUPLICATE_EXPORTED_IMAGE; } /* Blob checksums must be known before proceeding. */ ret = wim_checksum_unhashed_blobs(src_wim); if (ret) return ret; ret = wim_checksum_unhashed_blobs(dest_wim); if (ret) return ret; /* Enable rollbacks */ for_blob_in_table(dest_wim->blob_table, blob_set_not_exported, NULL); /* Export each requested image. */ for (src_image = start_src_image; src_image <= end_src_image; src_image++) { const tchar *next_dest_name, *next_dest_description; struct wim_image_metadata *src_imd; struct wim_inode *inode; /* Determine destination image name and description. */ if (export_flags & WIMLIB_EXPORT_FLAG_NO_NAMES) next_dest_name = NULL; else if (dest_name) next_dest_name = dest_name; else next_dest_name = wimlib_get_image_name(src_wim, src_image); if (export_flags & WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS) next_dest_description = NULL; else if (dest_description) next_dest_description = dest_description; else next_dest_description = wimlib_get_image_description(src_wim, src_image); /* Check for name conflict. */ if (wimlib_image_name_in_use(dest_wim, next_dest_name)) { ERROR("There is already an image named \"%"TS"\" " "in the destination WIM", next_dest_name); ret = WIMLIB_ERR_IMAGE_NAME_COLLISION; goto out_rollback; } /* Load metadata for source image into memory. */ ret = select_wim_image(src_wim, src_image); if (ret) goto out_rollback; src_imd = wim_get_current_image_metadata(src_wim); /* Iterate through inodes in the source image and export their * blobs into the destination WIM. */ image_for_each_inode(inode, src_imd) { ret = inode_export_blobs(inode, src_wim->blob_table, dest_wim->blob_table, export_flags & WIMLIB_EXPORT_FLAG_GIFT); if (ret) goto out_rollback; } /* Export XML information into the destination WIM. */ ret = xml_export_image(src_wim->xml_info, src_image, dest_wim->xml_info, next_dest_name, next_dest_description, export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT); if (ret) goto out_rollback; /* Reference the source image metadata from the destination WIM. */ ret = append_image_metadata(dest_wim, src_imd); if (ret) goto out_rollback; src_imd->refcnt++; } /* Image export complete. Finish by setting any needed special metadata * on the destination WIM. */ if (src_wim->hdr.flags & WIM_HDR_FLAG_RP_FIX) dest_wim->hdr.flags |= WIM_HDR_FLAG_RP_FIX; for (src_image = start_src_image; src_image <= end_src_image; src_image++) { int dst_image = orig_dest_image_count + 1 + (src_image - start_src_image); if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) && (!all_images || src_image == src_wim->hdr.boot_idx)) dest_wim->hdr.boot_idx = dst_image; } return 0; out_rollback: while ((image = xml_get_image_count(dest_wim->xml_info)) > orig_dest_image_count) { xml_delete_image(dest_wim->xml_info, image); } while (dest_wim->hdr.image_count > orig_dest_image_count) { put_image_metadata(dest_wim->image_metadata[ --dest_wim->hdr.image_count]); } for_blob_in_table(dest_wim->blob_table, blob_rollback_export, dest_wim->blob_table); return ret; } wimlib-1.13.1/src/metadata_resource.c0000644000175000017500000001534413230712610014425 00000000000000/* * metadata_resource.c */ /* * Copyright (C) 2012, 2013 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib/assert.h" #include "wimlib/blob_table.h" #include "wimlib/dentry.h" #include "wimlib/error.h" #include "wimlib/metadata.h" #include "wimlib/resource.h" #include "wimlib/security.h" #include "wimlib/write.h" /* Fix the security ID for every inode to be either -1 or in bounds. */ static void fix_security_ids(struct wim_image_metadata *imd, const u32 num_entries) { struct wim_inode *inode; unsigned long invalid_count = 0; image_for_each_inode(inode, imd) { if ((u32)inode->i_security_id >= num_entries) { if (inode->i_security_id >= 0) invalid_count++; inode->i_security_id = -1; } } if (invalid_count) WARNING("%lu inodes had invalid security IDs", invalid_count); } /* * Reads and parses a metadata resource for an image in the WIM file. * * @imd: * Pointer to the image metadata structure for the image whose metadata * resource we are reading. Its `metadata_blob' member specifies the blob * table entry for the metadata resource. The rest of the image metadata * entry will be filled in by this function. * * Return values: * WIMLIB_ERR_SUCCESS (0) * WIMLIB_ERR_INVALID_METADATA_RESOURCE * WIMLIB_ERR_NOMEM * WIMLIB_ERR_READ * WIMLIB_ERR_UNEXPECTED_END_OF_FILE * WIMLIB_ERR_DECOMPRESSION */ int read_metadata_resource(struct wim_image_metadata *imd) { const struct blob_descriptor *metadata_blob; void *buf; int ret; u8 hash[SHA1_HASH_SIZE]; struct wim_security_data *sd; struct wim_dentry *root; metadata_blob = imd->metadata_blob; /* Read the metadata resource into memory. (It may be compressed.) */ ret = read_blob_into_alloc_buf(metadata_blob, &buf); if (ret) return ret; /* Checksum the metadata resource. */ sha1_buffer(buf, metadata_blob->size, hash); if (!hashes_equal(metadata_blob->hash, hash)) { ERROR("Metadata resource is corrupted " "(invalid SHA-1 message digest)!"); ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE; goto out_free_buf; } /* Parse the metadata resource. * * Notes: The metadata resource consists of the security data, followed * by the directory entry for the root directory, followed by all the * other directory entries in the filesystem. The subdir offset field * of each directory entry gives the start of its child entries from the * beginning of the metadata resource. An end-of-directory is signaled * by a directory entry of length '0', really of length 8, because * that's how long the 'length' field is. */ ret = read_wim_security_data(buf, metadata_blob->size, &sd); if (ret) goto out_free_buf; ret = read_dentry_tree(buf, metadata_blob->size, sd->total_length, &root); if (ret) goto out_free_security_data; /* We have everything we need from the buffer now. */ FREE(buf); buf = NULL; /* Calculate and validate inodes. */ ret = dentry_tree_fix_inodes(root, &imd->inode_list); if (ret) goto out_free_dentry_tree; fix_security_ids(imd, sd->num_entries); /* Success; fill in the image_metadata structure. */ imd->root_dentry = root; imd->security_data = sd; INIT_LIST_HEAD(&imd->unhashed_blobs); return 0; out_free_dentry_tree: free_dentry_tree(root, NULL); out_free_security_data: free_wim_security_data(sd); out_free_buf: FREE(buf); return ret; } static void recalculate_security_data_length(struct wim_security_data *sd) { u32 total_length = sizeof(u64) * sd->num_entries + 2 * sizeof(u32); for (u32 i = 0; i < sd->num_entries; i++) total_length += sd->sizes[i]; sd->total_length = ALIGN(total_length, 8); } static int prepare_metadata_resource(WIMStruct *wim, int image, u8 **buf_ret, size_t *len_ret) { u8 *buf; u8 *p; int ret; u64 subdir_offset; struct wim_dentry *root; size_t len; struct wim_security_data *sd; struct wim_image_metadata *imd; ret = select_wim_image(wim, image); if (ret) return ret; imd = wim->image_metadata[image - 1]; root = imd->root_dentry; sd = imd->security_data; if (!root) { /* Empty image; create a dummy root. */ ret = new_filler_directory(&root); if (ret) return ret; imd->root_dentry = root; } /* The offset of the first child of the root dentry is equal to the * total length of the security data, plus the total length of the root * dentry, plus 8 bytes for an end-of-directory entry following the root * dentry (shouldn't really be needed, but just in case...) */ recalculate_security_data_length(sd); subdir_offset = sd->total_length + dentry_out_total_length(root) + 8; /* Calculate the subdirectory offsets for the entire dentry tree. */ calculate_subdir_offsets(root, &subdir_offset); /* Total length of the metadata resource (uncompressed). */ len = subdir_offset; /* Allocate a buffer to contain the uncompressed metadata resource. */ buf = NULL; if (likely(len == subdir_offset)) buf = MALLOC(len); if (!buf) { ERROR("Failed to allocate %"PRIu64" bytes for " "metadata resource", subdir_offset); return WIMLIB_ERR_NOMEM; } /* Write the security data into the resource buffer. */ p = write_wim_security_data(sd, buf); /* Write the dentry tree into the resource buffer. */ p = write_dentry_tree(root, p); /* We MUST have exactly filled the buffer; otherwise we calculated its * size incorrectly or wrote the data incorrectly. */ wimlib_assert(p - buf == len); *buf_ret = buf; *len_ret = len; return 0; } int write_metadata_resource(WIMStruct *wim, int image, int write_resource_flags) { int ret; u8 *buf; size_t len; struct wim_image_metadata *imd; ret = prepare_metadata_resource(wim, image, &buf, &len); if (ret) return ret; imd = wim->image_metadata[image - 1]; /* Write the metadata resource to the output WIM using the proper * compression type, in the process updating the blob descriptor for the * metadata resource. */ ret = write_wim_resource_from_buffer(buf, len, true, &wim->out_fd, wim->out_compression_type, wim->out_chunk_size, &imd->metadata_blob->out_reshdr, imd->metadata_blob->hash, write_resource_flags); FREE(buf); return ret; } wimlib-1.13.1/src/progress.c0000644000175000017500000000301413160354225012577 00000000000000/* * progress.c */ /* * Copyright (C) 2014 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib/progress.h" int report_error(wimlib_progress_func_t progfunc, void *progctx, int error_code, const tchar *path) { int ret; union wimlib_progress_info progress; tchar *cookie; if (error_code == WIMLIB_ERR_SUCCESS || error_code == WIMLIB_ERR_ABORTED_BY_PROGRESS || error_code == WIMLIB_ERR_UNKNOWN_PROGRESS_STATUS) return error_code; progress.handle_error.path = path; progress.handle_error.error_code = error_code; progress.handle_error.will_ignore = false; cookie = progress_get_win32_path(path); ret = call_progress(progfunc, WIMLIB_PROGRESS_MSG_HANDLE_ERROR, &progress, progctx); progress_put_win32_path(cookie); if (ret) return ret; if (!progress.handle_error.will_ignore) return error_code; return 0; } wimlib-1.13.1/src/compress_common.c0000644000175000017500000005776513160354224014163 00000000000000/* * compress_common.c * * Code for compression shared among multiple compression formats. * * The following copying information applies to this specific source code file: * * Written in 2012-2014 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include "wimlib/compress_common.h" #include "wimlib/util.h" /* Given the binary tree node A[subtree_idx] whose children already * satisfy the maxheap property, swap the node with its greater child * until it is greater than both its children, so that the maxheap * property is satisfied in the subtree rooted at A[subtree_idx]. */ static void heapify_subtree(u32 A[], unsigned length, unsigned subtree_idx) { unsigned parent_idx; unsigned child_idx; u32 v; v = A[subtree_idx]; parent_idx = subtree_idx; while ((child_idx = parent_idx * 2) <= length) { if (child_idx < length && A[child_idx + 1] > A[child_idx]) child_idx++; if (v >= A[child_idx]) break; A[parent_idx] = A[child_idx]; parent_idx = child_idx; } A[parent_idx] = v; } /* Rearrange the array 'A' so that it satisfies the maxheap property. * 'A' uses 1-based indices, so the children of A[i] are A[i*2] and A[i*2 + 1]. */ static void heapify_array(u32 A[], unsigned length) { for (unsigned subtree_idx = length / 2; subtree_idx >= 1; subtree_idx--) heapify_subtree(A, length, subtree_idx); } /* Sort the array 'A', which contains 'length' unsigned 32-bit integers. */ static void heapsort(u32 A[], unsigned length) { A--; /* Use 1-based indices */ heapify_array(A, length); while (length >= 2) { swap(A[1], A[length]); length--; heapify_subtree(A, length, 1); } } #define NUM_SYMBOL_BITS 10 #define SYMBOL_MASK ((1 << NUM_SYMBOL_BITS) - 1) /* * Sort the symbols primarily by frequency and secondarily by symbol * value. Discard symbols with zero frequency and fill in an array with * the remaining symbols, along with their frequencies. The low * NUM_SYMBOL_BITS bits of each array entry will contain the symbol * value, and the remaining bits will contain the frequency. * * @num_syms * Number of symbols in the alphabet. * Can't be greater than (1 << NUM_SYMBOL_BITS). * * @freqs[num_syms] * The frequency of each symbol. * * @lens[num_syms] * An array that eventually will hold the length of each codeword. * This function only fills in the codeword lengths for symbols that * have zero frequency, which are not well defined per se but will * be set to 0. * * @symout[num_syms] * The output array, described above. * * Returns the number of entries in 'symout' that were filled. This is * the number of symbols that have nonzero frequency. */ static unsigned sort_symbols(unsigned num_syms, const u32 freqs[restrict], u8 lens[restrict], u32 symout[restrict]) { unsigned num_used_syms; unsigned num_counters; /* We rely on heapsort, but with an added optimization. Since * it's common for most symbol frequencies to be low, we first do * a count sort using a limited number of counters. High * frequencies will be counted in the last counter, and only they * will be sorted with heapsort. * * Note: with more symbols, it is generally beneficial to have more * counters. About 1 counter per 4 symbols seems fast. * * Note: I also tested radix sort, but even for large symbol * counts (> 255) and frequencies bounded at 16 bits (enabling * radix sort by just two base-256 digits), it didn't seem any * faster than the method implemented here. * * Note: I tested the optimized quicksort implementation from * glibc (with indirection overhead removed), but it was only * marginally faster than the simple heapsort implemented here. * * Tests were done with building the codes for LZX. Results may * vary for different compression algorithms...! */ num_counters = ALIGN(DIV_ROUND_UP(num_syms, 4), 4); unsigned counters[num_counters]; memset(counters, 0, sizeof(counters)); /* Count the frequencies. */ for (unsigned sym = 0; sym < num_syms; sym++) counters[min(freqs[sym], num_counters - 1)]++; /* Make the counters cumulative, ignoring the zero-th, which * counted symbols with zero frequency. As a side effect, this * calculates the number of symbols with nonzero frequency. */ num_used_syms = 0; for (unsigned i = 1; i < num_counters; i++) { unsigned count = counters[i]; counters[i] = num_used_syms; num_used_syms += count; } /* Sort nonzero-frequency symbols using the counters. At the * same time, set the codeword lengths of zero-frequency symbols * to 0. */ for (unsigned sym = 0; sym < num_syms; sym++) { u32 freq = freqs[sym]; if (freq != 0) { symout[counters[min(freq, num_counters - 1)]++] = sym | (freq << NUM_SYMBOL_BITS); } else { lens[sym] = 0; } } /* Sort the symbols counted in the last counter. */ heapsort(symout + counters[num_counters - 2], counters[num_counters - 1] - counters[num_counters - 2]); return num_used_syms; } /* * Build the Huffman tree. * * This is an optimized implementation that * (a) takes advantage of the frequencies being already sorted; * (b) only generates non-leaf nodes, since the non-leaf nodes of a * Huffman tree are sufficient to generate a canonical code; * (c) Only stores parent pointers, not child pointers; * (d) Produces the nodes in the same memory used for input * frequency information. * * Array 'A', which contains 'sym_count' entries, is used for both input * and output. For this function, 'sym_count' must be at least 2. * * For input, the array must contain the frequencies of the symbols, * sorted in increasing order. Specifically, each entry must contain a * frequency left shifted by NUM_SYMBOL_BITS bits. Any data in the low * NUM_SYMBOL_BITS bits of the entries will be ignored by this function. * Although these bits will, in fact, contain the symbols that correspond * to the frequencies, this function is concerned with frequencies only * and keeps the symbols as-is. * * For output, this function will produce the non-leaf nodes of the * Huffman tree. These nodes will be stored in the first (sym_count - 1) * entries of the array. Entry A[sym_count - 2] will represent the root * node. Each other node will contain the zero-based index of its parent * node in 'A', left shifted by NUM_SYMBOL_BITS bits. The low * NUM_SYMBOL_BITS bits of each entry in A will be kept as-is. Again, * note that although these low bits will, in fact, contain a symbol * value, this symbol will have *no relationship* with the Huffman tree * node that happens to occupy the same slot. This is because this * implementation only generates the non-leaf nodes of the tree. */ static void build_tree(u32 A[], unsigned sym_count) { /* Index, in 'A', of next lowest frequency symbol that has not * yet been processed. */ unsigned i = 0; /* Index, in 'A', of next lowest frequency parentless non-leaf * node; or, if equal to 'e', then no such node exists yet. */ unsigned b = 0; /* Index, in 'A', of next node to allocate as a non-leaf. */ unsigned e = 0; do { unsigned m, n; u32 freq_shifted; /* Choose the two next lowest frequency entries. */ if (i != sym_count && (b == e || (A[i] >> NUM_SYMBOL_BITS) <= (A[b] >> NUM_SYMBOL_BITS))) m = i++; else m = b++; if (i != sym_count && (b == e || (A[i] >> NUM_SYMBOL_BITS) <= (A[b] >> NUM_SYMBOL_BITS))) n = i++; else n = b++; /* Allocate a non-leaf node and link the entries to it. * * If we link an entry that we're visiting for the first * time (via index 'i'), then we're actually linking a * leaf node and it will have no effect, since the leaf * will be overwritten with a non-leaf when index 'e' * catches up to it. But it's not any slower to * unconditionally set the parent index. * * We also compute the frequency of the non-leaf node as * the sum of its two children's frequencies. */ freq_shifted = (A[m] & ~SYMBOL_MASK) + (A[n] & ~SYMBOL_MASK); A[m] = (A[m] & SYMBOL_MASK) | (e << NUM_SYMBOL_BITS); A[n] = (A[n] & SYMBOL_MASK) | (e << NUM_SYMBOL_BITS); A[e] = (A[e] & SYMBOL_MASK) | freq_shifted; e++; } while (sym_count - e > 1); /* When just one entry remains, it is a "leaf" that was * linked to some other node. We ignore it, since the * rest of the array contains the non-leaves which we * need. (Note that we're assuming the cases with 0 or 1 * symbols were handled separately.) */ } /* * Given the stripped-down Huffman tree constructed by build_tree(), * determine the number of codewords that should be assigned each * possible length, taking into account the length-limited constraint. * * @A * The array produced by build_tree(), containing parent index * information for the non-leaf nodes of the Huffman tree. Each * entry in this array is a node; a node's parent always has a * greater index than that node itself. This function will * overwrite the parent index information in this array, so * essentially it will destroy the tree. However, the data in the * low NUM_SYMBOL_BITS of each entry will be preserved. * * @root_idx * The 0-based index of the root node in 'A', and consequently one * less than the number of tree node entries in 'A'. (Or, really 2 * less than the actual length of 'A'.) * * @len_counts * An array of length ('max_codeword_len' + 1) in which the number of * codewords having each length <= max_codeword_len will be * returned. * * @max_codeword_len * The maximum permissible codeword length. */ static void compute_length_counts(u32 A[restrict], unsigned root_idx, unsigned len_counts[restrict], unsigned max_codeword_len) { /* The key observations are: * * (1) We can traverse the non-leaf nodes of the tree, always * visiting a parent before its children, by simply iterating * through the array in reverse order. Consequently, we can * compute the depth of each node in one pass, overwriting the * parent indices with depths. * * (2) We can initially assume that in the real Huffman tree, * both children of the root are leaves. This corresponds to two * codewords of length 1. Then, whenever we visit a (non-leaf) * node during the traversal, we modify this assumption to * account for the current node *not* being a leaf, but rather * its two children being leaves. This causes the loss of one * codeword for the current depth and the addition of two * codewords for the current depth plus one. * * (3) We can handle the length-limited constraint fairly easily * by simply using the largest length available when a depth * exceeds max_codeword_len. */ for (unsigned len = 0; len <= max_codeword_len; len++) len_counts[len] = 0; len_counts[1] = 2; /* Set the root node's depth to 0. */ A[root_idx] &= SYMBOL_MASK; for (int node = root_idx - 1; node >= 0; node--) { /* Calculate the depth of this node. */ unsigned parent = A[node] >> NUM_SYMBOL_BITS; unsigned parent_depth = A[parent] >> NUM_SYMBOL_BITS; unsigned depth = parent_depth + 1; unsigned len = depth; /* Set the depth of this node so that it is available * when its children (if any) are processed. */ A[node] = (A[node] & SYMBOL_MASK) | (depth << NUM_SYMBOL_BITS); /* If needed, decrease the length to meet the * length-limited constraint. This is not the optimal * method for generating length-limited Huffman codes! * But it should be good enough. */ if (len >= max_codeword_len) { len = max_codeword_len; do { len--; } while (len_counts[len] == 0); } /* Account for the fact that we have a non-leaf node at * the current depth. */ len_counts[len]--; len_counts[len + 1] += 2; } } /* * Generate the codewords for a canonical Huffman code. * * @A * The output array for codewords. In addition, initially this * array must contain the symbols, sorted primarily by frequency and * secondarily by symbol value, in the low NUM_SYMBOL_BITS bits of * each entry. * * @len * Output array for codeword lengths. * * @len_counts * An array that provides the number of codewords that will have * each possible length <= max_codeword_len. * * @max_codeword_len * Maximum length, in bits, of each codeword. * * @num_syms * Number of symbols in the alphabet, including symbols with zero * frequency. This is the length of the 'A' and 'len' arrays. */ static void gen_codewords(u32 A[restrict], u8 lens[restrict], const unsigned len_counts[restrict], unsigned max_codeword_len, unsigned num_syms) { u32 next_codewords[max_codeword_len + 1]; /* Given the number of codewords that will have each length, * assign codeword lengths to symbols. We do this by assigning * the lengths in decreasing order to the symbols sorted * primarily by increasing frequency and secondarily by * increasing symbol value. */ for (unsigned i = 0, len = max_codeword_len; len >= 1; len--) { unsigned count = len_counts[len]; while (count--) lens[A[i++] & SYMBOL_MASK] = len; } /* Generate the codewords themselves. We initialize the * 'next_codewords' array to provide the lexicographically first * codeword of each length, then assign codewords in symbol * order. This produces a canonical code. */ next_codewords[0] = 0; next_codewords[1] = 0; for (unsigned len = 2; len <= max_codeword_len; len++) next_codewords[len] = (next_codewords[len - 1] + len_counts[len - 1]) << 1; for (unsigned sym = 0; sym < num_syms; sym++) A[sym] = next_codewords[lens[sym]]++; } /* * --------------------------------------------------------------------- * make_canonical_huffman_code() * --------------------------------------------------------------------- * * Given an alphabet and the frequency of each symbol in it, construct a * length-limited canonical Huffman code. * * @num_syms * The number of symbols in the alphabet. The symbols are the * integers in the range [0, num_syms - 1]. This parameter must be * at least 2 and can't be greater than (1 << NUM_SYMBOL_BITS). * * @max_codeword_len * The maximum permissible codeword length. * * @freqs * An array of @num_syms entries, each of which specifies the * frequency of the corresponding symbol. It is valid for some, * none, or all of the frequencies to be 0. * * @lens * An array of @num_syms entries in which this function will return * the length, in bits, of the codeword assigned to each symbol. * Symbols with 0 frequency will not have codewords per se, but * their entries in this array will be set to 0. No lengths greater * than @max_codeword_len will be assigned. * * @codewords * An array of @num_syms entries in which this function will return * the codeword for each symbol, right-justified and padded on the * left with zeroes. Codewords for symbols with 0 frequency will be * undefined. * * --------------------------------------------------------------------- * * This function builds a length-limited canonical Huffman code. * * A length-limited Huffman code contains no codewords longer than some * specified length, and has exactly (with some algorithms) or * approximately (with the algorithm used here) the minimum weighted path * length from the root, given this constraint. * * A canonical Huffman code satisfies the properties that a longer * codeword never lexicographically precedes a shorter codeword, and the * lexicographic ordering of codewords of the same length is the same as * the lexicographic ordering of the corresponding symbols. A canonical * Huffman code, or more generally a canonical prefix code, can be * reconstructed from only a list containing the codeword length of each * symbol. * * The classic algorithm to generate a Huffman code creates a node for * each symbol, then inserts these nodes into a min-heap keyed by symbol * frequency. Then, repeatedly, the two lowest-frequency nodes are * removed from the min-heap and added as the children of a new node * having frequency equal to the sum of its two children, which is then * inserted into the min-heap. When only a single node remains in the * min-heap, it is the root of the Huffman tree. The codeword for each * symbol is determined by the path needed to reach the corresponding * node from the root. Descending to the left child appends a 0 bit, * whereas descending to the right child appends a 1 bit. * * The classic algorithm is relatively easy to understand, but it is * subject to a number of inefficiencies. In practice, it is fastest to * first sort the symbols by frequency. (This itself can be subject to * an optimization based on the fact that most frequencies tend to be * low.) At the same time, we sort secondarily by symbol value, which * aids the process of generating a canonical code. Then, during tree * construction, no heap is necessary because both the leaf nodes and the * unparented non-leaf nodes can be easily maintained in sorted order. * Consequently, there can never be more than two possibilities for the * next-lowest-frequency node. * * In addition, because we're generating a canonical code, we actually * don't need the leaf nodes of the tree at all, only the non-leaf nodes. * This is because for canonical code generation we don't need to know * where the symbols are in the tree. Rather, we only need to know how * many leaf nodes have each depth (codeword length). And this * information can, in fact, be quickly generated from the tree of * non-leaves only. * * Furthermore, we can build this stripped-down Huffman tree directly in * the array in which the codewords are to be generated, provided that * these array slots are large enough to hold a symbol and frequency * value. * * Still furthermore, we don't even need to maintain explicit child * pointers. We only need the parent pointers, and even those can be * overwritten in-place with depth information as part of the process of * extracting codeword lengths from the tree. So in summary, we do NOT * need a big structure like: * * struct huffman_tree_node { * unsigned int symbol; * unsigned int frequency; * unsigned int depth; * struct huffman_tree_node *left_child; * struct huffman_tree_node *right_child; * }; * * * ... which often gets used in "naive" implementations of Huffman code * generation. * * Most of these optimizations are based on the implementation in 7-Zip * (source file: C/HuffEnc.c), which has been placed in the public domain * by Igor Pavlov. But I've rewritten the code with extensive comments, * as it took me a while to figure out what it was doing...! * * --------------------------------------------------------------------- * * NOTE: in general, the same frequencies can be used to generate * different length-limited canonical Huffman codes. One choice we have * is during tree construction, when we must decide whether to prefer a * leaf or non-leaf when there is a tie in frequency. Another choice we * have is how to deal with codewords that would exceed @max_codeword_len * bits in length. Both of these choices affect the resulting codeword * lengths, which otherwise can be mapped uniquely onto the resulting * canonical Huffman code. * * Normally, there is no problem with choosing one valid code over * another, provided that they produce similar compression ratios. * However, the LZMS compression format uses adaptive Huffman coding. It * requires that both the decompressor and compressor build a canonical * code equivalent to that which can be generated by using the classic * Huffman tree construction algorithm and always processing leaves * before non-leaves when there is a frequency tie. Therefore, we make * sure to do this. This method also has the advantage of sometimes * shortening the longest codeword that is generated. * * There also is the issue of how codewords longer than @max_codeword_len * are dealt with. Fortunately, for LZMS this is irrelevant because * because for the LZMS alphabets no codeword can ever exceed * LZMS_MAX_CODEWORD_LEN (= 15). Since the LZMS algorithm regularly * halves all frequencies, the frequencies cannot become high enough for * a length 16 codeword to be generated. Specifically, I think that if * ties are broken in favor of non-leaves (as we do), the lowest total * frequency that would give a length-16 codeword would be the sum of the * frequencies 1 1 1 3 4 7 11 18 29 47 76 123 199 322 521 843 1364, which * is 3570. And in LZMS we can't get a frequency that high based on the * alphabet sizes, rebuild frequencies, and scaling factors. This * worst-case scenario is based on the following degenerate case (only * the bottom of the tree shown): * * ... * 17 * / \ * 10 7 * / \ * 6 4 * / \ * 3 3 * / \ * 2 1 * / \ * 1 1 * * Excluding the first leaves (those with value 1), each leaf value must * be greater than the non-leaf up 1 and down 2 from it; otherwise that * leaf would have taken precedence over that non-leaf and been combined * with the leaf below, thereby decreasing the height compared to that * shown. * * Interesting fact: if we were to instead prioritize non-leaves over * leaves, then the worst case frequencies would be the Fibonacci * sequence, plus an extra frequency of 1. In this hypothetical * scenario, it would be slightly easier for longer codewords to be * generated. */ void make_canonical_huffman_code(unsigned num_syms, unsigned max_codeword_len, const u32 freqs[restrict], u8 lens[restrict], u32 codewords[restrict]) { u32 *A = codewords; unsigned num_used_syms; /* We begin by sorting the symbols primarily by frequency and * secondarily by symbol value. As an optimization, the array * used for this purpose ('A') shares storage with the space in * which we will eventually return the codewords. */ num_used_syms = sort_symbols(num_syms, freqs, lens, A); /* 'num_used_syms' is the number of symbols with nonzero * frequency. This may be less than @num_syms. 'num_used_syms' * is also the number of entries in 'A' that are valid. Each * entry consists of a distinct symbol and a nonzero frequency * packed into a 32-bit integer. */ /* Handle special cases where only 0 or 1 symbols were used (had * nonzero frequency). */ if (unlikely(num_used_syms == 0)) { /* Code is empty. sort_symbols() already set all lengths * to 0, so there is nothing more to do. */ return; } if (unlikely(num_used_syms == 1)) { /* Only one symbol was used, so we only need one * codeword. But two codewords are needed to form the * smallest complete Huffman code, which uses codewords 0 * and 1. Therefore, we choose another symbol to which * to assign a codeword. We use 0 (if the used symbol is * not 0) or 1 (if the used symbol is 0). In either * case, the lesser-valued symbol must be assigned * codeword 0 so that the resulting code is canonical. */ unsigned sym = A[0] & SYMBOL_MASK; unsigned nonzero_idx = sym ? sym : 1; codewords[0] = 0; lens[0] = 1; codewords[nonzero_idx] = 1; lens[nonzero_idx] = 1; return; } /* Build a stripped-down version of the Huffman tree, sharing the * array 'A' with the symbol values. Then extract length counts * from the tree and use them to generate the final codewords. */ build_tree(A, num_used_syms); { unsigned len_counts[max_codeword_len + 1]; compute_length_counts(A, num_used_syms - 2, len_counts, max_codeword_len); gen_codewords(A, lens, len_counts, max_codeword_len, num_syms); } } wimlib-1.13.1/src/sha1.c0000644000175000017500000001616213160354225011577 00000000000000/* * sha1.c - implementation of the Secure Hash Algorithm version 1 (FIPS 180-1) * * The following copying information applies to this specific source code file: * * Written in 2014-2015 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib/endianness.h" #include "wimlib/sha1.h" #include "wimlib/unaligned.h" /* Dummy SHA-1 message digest of all 0's. This is used in the WIM format to * mean "SHA-1 not specified". */ const u8 zero_hash[20]; /* * Builds a hexadecimal string representation of a SHA-1 message digest. * * The output buffer must be at least 41 characters. */ void sprint_hash(const u8 hash[SHA1_HASH_SIZE], tchar strbuf[SHA1_HASH_SIZE * 2 + 1]) { int i; u8 high, low; for (i = 0; i < SHA1_HASH_SIZE; i++) { high = hash[i] >> 4; low = hash[i] & 0xF; strbuf[i * 2 + 0] = (high < 10 ? high + '0' : high - 10 + 'a'); strbuf[i * 2 + 1] = (low < 10 ? low + '0' : low - 10 + 'a'); } strbuf[i * 2] = 0; } /* If we use libcrypto (e.g. OpenSSL) then we get all the SHA-1 functions for * free. Otherwise we need to implement them ourselves. */ #ifndef WITH_LIBCRYPTO #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) #define blk0(i) (tmp[i] = be32_to_cpu(load_be32_unaligned(&(block)[(i) * 4]))) #define blk(i) (tmp[i & 15] = rol(tmp[(i + 13) & 15] ^ \ tmp[(i + 8) & 15] ^ \ tmp[(i + 2) & 15] ^ \ tmp[(i + 0) & 15], 1)) #define R0(v, w, x, y, z, i) \ z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \ w = rol(w, 30); #define R1(v, w, x, y, z, i) \ z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ w = rol(w, 30); #define R2(v, w, x, y, z, i) \ z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \ w = rol(w, 30); #define R3(v, w, x, y, z, i) \ z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ w = rol(w, 30); #define R4(v, w, x, y, z, i) \ z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ w = rol(w, 30); /* Hash a single 512-bit block. This is the core of the algorithm. */ static void sha1_transform_default(u32 state[5], const u8 block[64]) { u32 a, b, c, d, e; u32 tmp[16]; /* Copy ctx->state[] to working vars */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; /* 4 rounds of 20 operations each. Loop unrolled. */ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; } #ifdef ENABLE_SSSE3_SHA1 extern void sha1_transform_blocks_ssse3(u32 state[5], const void *data, size_t num_blocks); extern void sha1_transform_blocks_default(u32 state[5], const void *data, size_t num_blocks); # define sha1_transform_blocks sha1_transform_blocks_ssse3 #else # define sha1_transform_blocks sha1_transform_blocks_default #endif #ifndef ENABLE_SSSE3_SHA1 static #endif void sha1_transform_blocks_default(u32 state[5], const void *data, size_t num_blocks) { do { sha1_transform_default(state, data); data += 64; } while (--num_blocks); } /* Initializes the specified SHA-1 context. * * After sha1_init(), call sha1_update() zero or more times to provide the data * to be hashed. Then call sha1_final() to get the final hash. */ void sha1_init(SHA_CTX *ctx) { ctx->bytecount = 0; ctx->state[0] = 0x67452301; ctx->state[1] = 0xEFCDAB89; ctx->state[2] = 0x98BADCFE; ctx->state[3] = 0x10325476; ctx->state[4] = 0xC3D2E1F0; } /* Updates the SHA-1 context with @len bytes of data. */ void sha1_update(SHA_CTX *ctx, const void *data, size_t len) { unsigned buffered = ctx->bytecount & 63; ctx->bytecount += len; if (buffered) { /* Previous block is unfinished. */ if (len < 64 - buffered) { memcpy(&ctx->buffer[buffered], data, len); /* Previous block still unfinished. */ return; } else { memcpy(&ctx->buffer[buffered], data, 64 - buffered); /* Finished the previous block. */ sha1_transform_blocks(ctx->state, ctx->buffer, 1); data += 64 - buffered; len -= 64 - buffered; } } /* Process blocks directly from the input data. */ if (len / 64) { sha1_transform_blocks(ctx->state, data, len / 64); data += len & ~63; len &= 63; } /* Copy any remaining bytes to the buffer. */ if (len) memcpy(ctx->buffer, data, len); } /* Pad the message and generate the final SHA-1 message digest. */ void sha1_final(u8 md[20], SHA_CTX *ctx) { /* Logically, we must append 1 bit, then a variable number of 0 bits, * then the message length in bits as a big-endian integer, so that the * final length is a multiple of the block size. */ static const u8 padding[64] = {0x80, }; be64 finalcount = cpu_to_be64(ctx->bytecount << 3); sha1_update(ctx, padding, 64 - ((ctx->bytecount + 8) & 63)); sha1_update(ctx, &finalcount, 8); for (int i = 0; i < 5; i++) store_be32_unaligned(cpu_to_be32(ctx->state[i]), &md[i * 4]); } /* Calculate the SHA-1 message digest of the specified buffer. * @len is the buffer length in bytes. */ void sha1_buffer(const void *buffer, size_t len, u8 md[20]) { SHA_CTX ctx; sha1_init(&ctx); sha1_update(&ctx, buffer, len); sha1_final(md, &ctx); } #endif /* !WITH_LIBCRYPTO */ wimlib-1.13.1/src/update_image.c0000644000175000017500000011734613376102545013402 00000000000000/* * update_image.c - see description below */ /* * Copyright (C) 2013, 2014 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ /* * This file contains the implementation of wimlib_update_image(), which is one * of the two ways by which library users can make changes to a WIM image. (The * other way is by mounting an image read-write.) wimlib_update_image() is also * used in the implementation of wimlib_add_image(), since "create a WIM image * from this directory tree" is equivalent to "create an empty WIM image, then * update it to add this directory tree as the root". * * wimlib_update_image() processes a list of commands passed to it. Currently, * the following types of commands are supported: * * - Add a directory tree from an external source (filesystem or NTFS volume). * This can be used to add new files or to replace existing files. * - Delete a file or directory tree. * - Rename a file or directory tree. * * Not supported are creating links to existing files or changing metadata of * existing files. * * wimlib_update_image() is atomic. If it cannot complete successfully, then * all changes are rolled back and the WIMStruct is left unchanged. Rollback is * implemented by breaking the commands into primitive operations such as "link * this dentry tree here" which can be undone by doing the opposite operations * in reverse order. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include "wimlib/alloca.h" #include "wimlib/assert.h" #include "wimlib/blob_table.h" #include "wimlib/dentry.h" #include "wimlib/encoding.h" #include "wimlib/endianness.h" #include "wimlib/error.h" #include "wimlib/metadata.h" #include "wimlib/paths.h" #include "wimlib/progress.h" #include "wimlib/scan.h" #include "wimlib/test_support.h" #include "wimlib/xml_windows.h" /* Saved specification of a "primitive" update operation that was performed. */ struct update_primitive { enum { /* Unlinked a dentry from its parent directory. */ UNLINK_DENTRY, /* Linked a dentry into its parent directory. */ LINK_DENTRY, /* Changed the file name of a dentry. */ CHANGE_FILE_NAME, /* Changed the short name of a dentry. */ CHANGE_SHORT_NAME, } type; union { /* For UNLINK_DENTRY and LINK_DENTRY operations */ struct { /* Dentry that was linked or unlinked. */ struct wim_dentry *subject; /* For link operations, the directory into which * @subject was linked, or NULL if @subject was set as * the root of the image. * * For unlink operations, the directory from which * @subject was unlinked, or NULL if @subject was unset * as the root of the image. */ struct wim_dentry *parent; } link; /* For CHANGE_FILE_NAME and CHANGE_SHORT_NAME operations */ struct { /* Dentry that had its name changed. */ struct wim_dentry *subject; /* The old name. */ utf16lechar *old_name; } name; }; }; /* Chronological list of primitive operations that were executed for a single * logical update command, such as 'add', 'delete', or 'rename'. */ struct update_primitive_list { struct update_primitive *entries; struct update_primitive inline_entries[4]; size_t num_entries; size_t num_alloc_entries; }; /* Journal for managing the executing of zero or more logical update commands, * such as 'add', 'delete', or 'rename'. This allows either committing or * rolling back the commands. */ struct update_command_journal { /* Number of update commands this journal contains. */ size_t num_cmds; /* Index of currently executing update command. */ size_t cur_cmd; /* Location of the WIM image's root pointer. */ struct wim_dentry **root_p; /* Pointer to the blob table of the WIM (may needed for rollback) */ struct blob_table *blob_table; /* List of dentries that are currently unlinked from the WIM image. * These must be freed when no longer needed for commit or rollback. */ struct list_head orphans; /* Per-command logs. */ struct update_primitive_list cmd_prims[]; }; static void init_update_primitive_list(struct update_primitive_list *l) { l->entries = l->inline_entries; l->num_entries = 0; l->num_alloc_entries = ARRAY_LEN(l->inline_entries); } /* Allocates a new journal for managing the execution of up to @num_cmds update * commands. */ static struct update_command_journal * new_update_command_journal(size_t num_cmds, struct wim_dentry **root_p, struct blob_table *blob_table) { struct update_command_journal *j; j = MALLOC(sizeof(*j) + num_cmds * sizeof(j->cmd_prims[0])); if (j) { j->num_cmds = num_cmds; j->cur_cmd = 0; j->root_p = root_p; j->blob_table = blob_table; INIT_LIST_HEAD(&j->orphans); for (size_t i = 0; i < num_cmds; i++) init_update_primitive_list(&j->cmd_prims[i]); } return j; } /* Don't call this directly; use commit_update() or rollback_update() instead. */ static void free_update_command_journal(struct update_command_journal *j) { struct wim_dentry *orphan; /* Free orphaned dentry trees */ while (!list_empty(&j->orphans)) { orphan = list_first_entry(&j->orphans, struct wim_dentry, d_tmp_list); list_del(&orphan->d_tmp_list); free_dentry_tree(orphan, j->blob_table); } for (size_t i = 0; i < j->num_cmds; i++) if (j->cmd_prims[i].entries != j->cmd_prims[i].inline_entries) FREE(j->cmd_prims[i].entries); FREE(j); } /* Add the entry @prim to the update command journal @j. */ static int record_update_primitive(struct update_command_journal *j, struct update_primitive prim) { struct update_primitive_list *l; l = &j->cmd_prims[j->cur_cmd]; if (l->num_entries == l->num_alloc_entries) { struct update_primitive *new_entries; size_t new_num_alloc_entries; size_t new_size; new_num_alloc_entries = l->num_alloc_entries * 2; new_size = new_num_alloc_entries * sizeof(new_entries[0]); if (l->entries == l->inline_entries) { new_entries = MALLOC(new_size); if (!new_entries) return WIMLIB_ERR_NOMEM; memcpy(new_entries, l->inline_entries, sizeof(l->inline_entries)); } else { new_entries = REALLOC(l->entries, new_size); if (!new_entries) return WIMLIB_ERR_NOMEM; } l->entries = new_entries; l->num_alloc_entries = new_num_alloc_entries; } l->entries[l->num_entries++] = prim; return 0; } static void do_unlink(struct wim_dentry *subject, struct wim_dentry *parent, struct wim_dentry **root_p) { if (parent) { /* Unlink @subject from its @parent. */ wimlib_assert(subject->d_parent == parent); unlink_dentry(subject); } else { /* Unset @subject as the root of the image. */ *root_p = NULL; } subject->d_parent = subject; } static void do_link(struct wim_dentry *subject, struct wim_dentry *parent, struct wim_dentry **root_p) { if (parent) { /* Link @subject to its @parent */ struct wim_dentry *existing; existing = dentry_add_child(parent, subject); wimlib_assert(!existing); } else { /* Set @subject as root of the image */ *root_p = subject; } } /* Undo a link operation. */ static void rollback_link(struct wim_dentry *subject, struct wim_dentry *parent, struct wim_dentry **root_p, struct list_head *orphans) { /* Unlink is the opposite of link */ do_unlink(subject, parent, root_p); /* @subject is now unlinked. Add it to orphans. */ list_add(&subject->d_tmp_list, orphans); subject->d_is_orphan = 1; } /* Undo an unlink operation. */ static void rollback_unlink(struct wim_dentry *subject, struct wim_dentry *parent, struct wim_dentry **root_p) { /* Link is the opposite of unlink */ do_link(subject, parent, root_p); /* @subject is no longer unlinked. Delete it from orphans. */ list_del(&subject->d_tmp_list); subject->d_is_orphan = 0; } /* Rollback a name change operation. */ static void rollback_name_change(utf16lechar *old_name, utf16lechar **name_ptr, u16 *name_nbytes_ptr) { /* Free the new name, then replace it with the old name. */ FREE(*name_ptr); if (old_name) { *name_ptr = old_name; *name_nbytes_ptr = utf16le_len_bytes(old_name); } else { *name_ptr = NULL; *name_nbytes_ptr = 0; } } /* Rollback a primitive update operation. */ static void rollback_update_primitive(const struct update_primitive *prim, struct wim_dentry **root_p, struct list_head *orphans) { switch (prim->type) { case LINK_DENTRY: rollback_link(prim->link.subject, prim->link.parent, root_p, orphans); break; case UNLINK_DENTRY: rollback_unlink(prim->link.subject, prim->link.parent, root_p); break; case CHANGE_FILE_NAME: rollback_name_change(prim->name.old_name, &prim->name.subject->d_name, &prim->name.subject->d_name_nbytes); break; case CHANGE_SHORT_NAME: rollback_name_change(prim->name.old_name, &prim->name.subject->d_short_name, &prim->name.subject->d_short_name_nbytes); break; } } /* Rollback a logical update command */ static void rollback_update_command(const struct update_primitive_list *l, struct wim_dentry **root_p, struct list_head *orphans) { size_t i = l->num_entries; /* Rollback each primitive operation, in reverse order. */ while (i--) rollback_update_primitive(&l->entries[i], root_p, orphans); } /****************************************************************************/ /* Link @subject into the directory @parent; or, if @parent is NULL, set * @subject as the root of the WIM image. * * This is the journaled version, so it can be rolled back. */ static int journaled_link(struct update_command_journal *j, struct wim_dentry *subject, struct wim_dentry *parent) { struct update_primitive prim; int ret; prim.type = LINK_DENTRY; prim.link.subject = subject; prim.link.parent = parent; ret = record_update_primitive(j, prim); if (ret) return ret; do_link(subject, parent, j->root_p); if (subject->d_is_orphan) { list_del(&subject->d_tmp_list); subject->d_is_orphan = 0; } return 0; } /* Unlink @subject from the WIM image. * * This is the journaled version, so it can be rolled back. */ static int journaled_unlink(struct update_command_journal *j, struct wim_dentry *subject) { struct wim_dentry *parent; struct update_primitive prim; int ret; if (dentry_is_root(subject)) parent = NULL; else parent = subject->d_parent; prim.type = UNLINK_DENTRY; prim.link.subject = subject; prim.link.parent = parent; ret = record_update_primitive(j, prim); if (ret) return ret; do_unlink(subject, parent, j->root_p); list_add(&subject->d_tmp_list, &j->orphans); subject->d_is_orphan = 1; return 0; } /* Change the name of @dentry to @new_name_tstr. * * This is the journaled version, so it can be rolled back. */ static int journaled_change_name(struct update_command_journal *j, struct wim_dentry *dentry, const tchar *new_name_tstr) { int ret; utf16lechar *new_name; size_t new_name_nbytes; struct update_primitive prim; /* Set the long name. */ ret = tstr_to_utf16le(new_name_tstr, tstrlen(new_name_tstr) * sizeof(tchar), &new_name, &new_name_nbytes); if (ret) return ret; prim.type = CHANGE_FILE_NAME; prim.name.subject = dentry; prim.name.old_name = dentry->d_name; ret = record_update_primitive(j, prim); if (ret) { FREE(new_name); return ret; } dentry->d_name = new_name; dentry->d_name_nbytes = new_name_nbytes; /* Clear the short name. */ prim.type = CHANGE_SHORT_NAME; prim.name.subject = dentry; prim.name.old_name = dentry->d_short_name; ret = record_update_primitive(j, prim); if (ret) return ret; dentry->d_short_name = NULL; dentry->d_short_name_nbytes = 0; return 0; } static void next_command(struct update_command_journal *j) { j->cur_cmd++; } static void commit_update(struct update_command_journal *j) { for (size_t i = 0; i < j->num_cmds; i++) { for (size_t k = 0; k < j->cmd_prims[i].num_entries; k++) { if (j->cmd_prims[i].entries[k].type == CHANGE_FILE_NAME || j->cmd_prims[i].entries[k].type == CHANGE_SHORT_NAME) { FREE(j->cmd_prims[i].entries[k].name.old_name); } } } free_update_command_journal(j); } static void rollback_update(struct update_command_journal *j) { /* Rollback each logical update command, in reverse order. */ size_t i = j->cur_cmd; if (i < j->num_cmds) i++; while (i--) rollback_update_command(&j->cmd_prims[i], j->root_p, &j->orphans); free_update_command_journal(j); } static int handle_conflict(struct wim_dentry *branch, struct wim_dentry *existing, struct update_command_journal *j, int add_flags, wimlib_progress_func_t progfunc, void *progctx) { bool branch_is_dir = dentry_is_directory(branch); bool existing_is_dir = dentry_is_directory(existing); if (branch_is_dir != existing_is_dir) { if (existing_is_dir) { ERROR("\"%"TS"\" is a directory!\n" " Specify the path at which " "to place the file inside this directory.", dentry_full_path(existing)); return WIMLIB_ERR_IS_DIRECTORY; } else { ERROR("Can't place directory at \"%"TS"\" because " "a nondirectory file already exists there!", dentry_full_path(existing)); return WIMLIB_ERR_NOTDIR; } } if (branch_is_dir) { /* Directory overlay */ while (dentry_has_children(branch)) { struct wim_dentry *new_child; struct wim_dentry *existing_child; int ret; new_child = dentry_any_child(branch); existing_child = get_dentry_child_with_utf16le_name(existing, new_child->d_name, new_child->d_name_nbytes, WIMLIB_CASE_PLATFORM_DEFAULT); unlink_dentry(new_child); if (existing_child) { ret = handle_conflict(new_child, existing_child, j, add_flags, progfunc, progctx); } else { ret = journaled_link(j, new_child, existing); } if (ret) { dentry_add_child(branch, new_child); return ret; } } free_dentry_tree(branch, j->blob_table); return 0; } else if (add_flags & WIMLIB_ADD_FLAG_NO_REPLACE) { /* Can't replace nondirectory file */ ERROR("Refusing to overwrite nondirectory file \"%"TS"\"", dentry_full_path(existing)); return WIMLIB_ERR_INVALID_OVERLAY; } else { /* Replace nondirectory file */ struct wim_dentry *parent; int ret; parent = existing->d_parent; ret = calculate_dentry_full_path(existing); if (ret) return ret; if (add_flags & WIMLIB_ADD_FLAG_VERBOSE) { union wimlib_progress_info info; info.replace.path_in_wim = existing->d_full_path; ret = call_progress(progfunc, WIMLIB_PROGRESS_MSG_REPLACE_FILE_IN_WIM, &info, progctx); if (ret) return ret; } ret = journaled_unlink(j, existing); if (ret) return ret; return journaled_link(j, branch, parent); } } static int do_attach_branch(struct wim_dentry *branch, const utf16lechar *target, struct update_command_journal *j, int add_flags, wimlib_progress_func_t progfunc, void *progctx) { struct wim_dentry *parent; struct wim_dentry *existing; const utf16lechar empty_name[1] = {0}; const utf16lechar *cur_component_name; size_t cur_component_nbytes; const utf16lechar *next_component_name; int ret; /* Attempt to create root directory before proceeding to the "real" * first component */ parent = NULL; existing = *j->root_p; cur_component_name = empty_name; cur_component_nbytes = 0; /* Skip leading slashes */ next_component_name = target; while (*next_component_name == cpu_to_le16(WIM_PATH_SEPARATOR)) next_component_name++; while (*next_component_name) { /* While not the last component ... */ const utf16lechar *end; if (existing) { /* Descend into existing directory */ if (!dentry_is_directory(existing)) { ERROR("\"%"TS"\" in the WIM image " "is not a directory!", dentry_full_path(existing)); return WIMLIB_ERR_NOTDIR; } } else { /* A parent directory of the target didn't exist. Make * the way by creating a filler directory. */ struct wim_dentry *filler; ret = new_filler_directory(&filler); if (ret) return ret; ret = dentry_set_name_utf16le(filler, cur_component_name, cur_component_nbytes); if (ret) { free_dentry(filler); return ret; } ret = journaled_link(j, filler, parent); if (ret) { free_dentry(filler); return ret; } existing = filler; } /* Advance to next component */ cur_component_name = next_component_name; end = cur_component_name + 1; while (*end && *end != cpu_to_le16(WIM_PATH_SEPARATOR)) end++; next_component_name = end; if (*end) { /* There will still be more components after this. */ do { } while (*++next_component_name == cpu_to_le16(WIM_PATH_SEPARATOR)); wimlib_assert(*next_component_name); /* No trailing slashes */ } else { /* This will be the last component */ next_component_name = end; } parent = existing; cur_component_nbytes = (end - cur_component_name) * sizeof(utf16lechar); existing = get_dentry_child_with_utf16le_name( parent, cur_component_name, cur_component_nbytes, WIMLIB_CASE_PLATFORM_DEFAULT); } /* Last component */ if (existing) { return handle_conflict(branch, existing, j, add_flags, progfunc, progctx); } else { return journaled_link(j, branch, parent); } } /* * Place the directory entry tree @branch at the path @target_tstr in the WIM * image. * * @target_tstr cannot contain trailing slashes, and all path separators must be * WIM_PATH_SEPARATOR. * * On success, @branch is committed to the journal @j. * Otherwise @branch is freed. * * The relevant @add_flags are WIMLIB_ADD_FLAG_NO_REPLACE and * WIMLIB_ADD_FLAG_VERBOSE. */ static int attach_branch(struct wim_dentry *branch, const tchar *target_tstr, struct update_command_journal *j, int add_flags, wimlib_progress_func_t progfunc, void *progctx) { int ret; const utf16lechar *target; ret = 0; if (unlikely(!branch)) goto out; ret = tstr_get_utf16le(target_tstr, &target); if (ret) goto out_free_branch; STATIC_ASSERT(WIM_PATH_SEPARATOR == OS_PREFERRED_PATH_SEPARATOR); ret = dentry_set_name(branch, path_basename(target_tstr)); if (ret) goto out_free_target; ret = do_attach_branch(branch, target, j, add_flags, progfunc, progctx); if (ret) goto out_free_target; /* branch was successfully committed to the journal */ branch = NULL; out_free_target: tstr_put_utf16le(target); out_free_branch: free_dentry_tree(branch, j->blob_table); out: return ret; } static const char wincfg[] = "[ExclusionList]\n" "/$ntfs.log\n" "/hiberfil.sys\n" "/pagefile.sys\n" "/swapfile.sys\n" "/System Volume Information\n" "/RECYCLER\n" "/$RECYCLE.BIN\n" "/$Recycle.Bin\n" "/Windows/CSC\n"; static const tchar *wimboot_cfgfile = WIMLIB_WIM_PATH_SEPARATOR_STRING T("Windows") WIMLIB_WIM_PATH_SEPARATOR_STRING T("System32") WIMLIB_WIM_PATH_SEPARATOR_STRING T("WimBootCompress.ini"); static int get_capture_config(const tchar *config_file, struct capture_config *config, int add_flags, const tchar *fs_source_path) { int ret; tchar *tmp_config_file = NULL; memset(config, 0, sizeof(*config)); /* For WIMBoot capture, check for default capture configuration file * unless one was explicitly specified. */ if (!config_file && (add_flags & WIMLIB_ADD_FLAG_WIMBOOT)) { /* XXX: Handle loading file correctly when in NTFS volume. */ size_t len = tstrlen(fs_source_path) + tstrlen(wimboot_cfgfile); tmp_config_file = MALLOC((len + 1) * sizeof(tchar)); struct stat st; tsprintf(tmp_config_file, T("%"TS"%"TS), fs_source_path, wimboot_cfgfile); if (!tstat(tmp_config_file, &st)) { config_file = tmp_config_file; add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG; } else { WARNING("\"%"TS"\" does not exist.\n" " Using default capture configuration!", tmp_config_file); } } if (add_flags & WIMLIB_ADD_FLAG_WINCONFIG) { /* Use Windows default. */ if (config_file) return WIMLIB_ERR_INVALID_PARAM; ret = read_capture_config(T("wincfg"), wincfg, sizeof(wincfg) - 1, config); } else if (config_file) { /* Use the specified configuration file. */ ret = read_capture_config(config_file, NULL, 0, config); } else { /* ... Or don't use any configuration file at all. No files * will be excluded from capture, all files will be compressed, * etc. */ ret = 0; } FREE(tmp_config_file); return ret; } static int execute_add_command(struct update_command_journal *j, WIMStruct *wim, const struct wimlib_update_command *add_cmd, struct wim_inode_table *inode_table, struct wim_sd_set *sd_set, struct list_head *unhashed_blobs) { int ret; int add_flags; tchar *fs_source_path; tchar *wim_target_path; const tchar *config_file; struct scan_params params; struct capture_config config; scan_tree_t scan_tree = platform_default_scan_tree; struct wim_dentry *branch; add_flags = add_cmd->add.add_flags; fs_source_path = add_cmd->add.fs_source_path; wim_target_path = add_cmd->add.wim_target_path; config_file = add_cmd->add.config_file; memset(¶ms, 0, sizeof(params)); #ifdef WITH_NTFS_3G if (add_flags & WIMLIB_ADD_FLAG_NTFS) scan_tree = ntfs_3g_build_dentry_tree; #endif #ifdef ENABLE_TEST_SUPPORT if (add_flags & WIMLIB_ADD_FLAG_GENERATE_TEST_DATA) scan_tree = generate_dentry_tree; #endif ret = get_capture_config(config_file, &config, add_flags, fs_source_path); if (ret) goto out; params.blob_table = wim->blob_table; params.unhashed_blobs = unhashed_blobs; params.inode_table = inode_table; params.sd_set = sd_set; params.config = &config; params.add_flags = add_flags; params.progfunc = wim->progfunc; params.progctx = wim->progctx; params.progress.scan.source = fs_source_path; params.progress.scan.wim_target_path = wim_target_path; ret = call_progress(params.progfunc, WIMLIB_PROGRESS_MSG_SCAN_BEGIN, ¶ms.progress, params.progctx); if (ret) goto out_destroy_config; if (WIMLIB_IS_WIM_ROOT_PATH(wim_target_path)) params.add_flags |= WIMLIB_ADD_FLAG_ROOT; ret = (*scan_tree)(&branch, fs_source_path, ¶ms); if (ret) goto out_destroy_config; ret = call_progress(params.progfunc, WIMLIB_PROGRESS_MSG_SCAN_END, ¶ms.progress, params.progctx); if (ret) { free_dentry_tree(branch, wim->blob_table); goto out_destroy_config; } if (WIMLIB_IS_WIM_ROOT_PATH(wim_target_path) && branch && !dentry_is_directory(branch)) { ERROR("\"%"TS"\" is not a directory!", fs_source_path); ret = WIMLIB_ERR_NOTDIR; free_dentry_tree(branch, wim->blob_table); goto out_destroy_config; } ret = attach_branch(branch, wim_target_path, j, add_flags, params.progfunc, params.progctx); if (ret) goto out_destroy_config; if (config_file && (add_flags & WIMLIB_ADD_FLAG_WIMBOOT) && WIMLIB_IS_WIM_ROOT_PATH(wim_target_path)) { params.add_flags = 0; params.progfunc = NULL; params.config = NULL; /* If a capture configuration file was explicitly specified when * capturing an image in WIMBoot mode, save it as * /Windows/System32/WimBootCompress.ini in the WIM image. */ ret = platform_default_scan_tree(&branch, config_file, ¶ms); if (ret) goto out_destroy_config; ret = attach_branch(branch, wimboot_cfgfile, j, 0, NULL, NULL); if (ret) goto out_destroy_config; } if (WIMLIB_IS_WIM_ROOT_PATH(wim_target_path)) { ret = set_windows_specific_info(wim); if (ret) goto out_destroy_config; } ret = 0; out_destroy_config: destroy_capture_config(&config); out: FREE(params.cur_path); return ret; } static int execute_delete_command(struct update_command_journal *j, WIMStruct *wim, const struct wimlib_update_command *delete_cmd) { int flags; const tchar *wim_path; struct wim_dentry *tree; flags = delete_cmd->delete_.delete_flags; wim_path = delete_cmd->delete_.wim_path; tree = get_dentry(wim, wim_path, WIMLIB_CASE_PLATFORM_DEFAULT); if (!tree) { /* Path to delete does not exist in the WIM. */ if (flags & WIMLIB_DELETE_FLAG_FORCE) { return 0; } else { ERROR("Path \"%"TS"\" does not exist in WIM image %d", wim_path, wim->current_image); return WIMLIB_ERR_PATH_DOES_NOT_EXIST; } } if (dentry_is_directory(tree) && !(flags & WIMLIB_DELETE_FLAG_RECURSIVE)) { ERROR("Path \"%"TS"\" in WIM image %d is a directory " "but a recursive delete was not requested", wim_path, wim->current_image); return WIMLIB_ERR_IS_DIRECTORY; } return journaled_unlink(j, tree); } static int free_dentry_full_path(struct wim_dentry *dentry, void *_ignore) { FREE(dentry->d_full_path); dentry->d_full_path = NULL; return 0; } /* Is @d1 a (possibly nonproper) ancestor of @d2? */ static bool is_ancestor(const struct wim_dentry *d1, const struct wim_dentry *d2) { for (;;) { if (d2 == d1) return true; if (dentry_is_root(d2)) return false; d2 = d2->d_parent; } } /* Rename a file or directory in the WIM. * * This returns a -errno value. * * The journal @j is optional. */ int rename_wim_path(WIMStruct *wim, const tchar *from, const tchar *to, CASE_SENSITIVITY_TYPE case_type, struct update_command_journal *j) { struct wim_dentry *src; struct wim_dentry *dst; struct wim_dentry *parent_of_dst; int ret; /* This rename() implementation currently only supports actual files * (not alternate data streams) */ src = get_dentry(wim, from, case_type); if (!src) return -errno; dst = get_dentry(wim, to, case_type); if (dst) { /* Destination file exists */ if (src == dst) /* Same file */ return 0; if (!dentry_is_directory(src)) { /* Cannot rename non-directory to directory. */ if (dentry_is_directory(dst)) return -EISDIR; } else { /* Cannot rename directory to a non-directory or a non-empty * directory */ if (!dentry_is_directory(dst)) return -ENOTDIR; if (dentry_has_children(dst)) return -ENOTEMPTY; } parent_of_dst = dst->d_parent; } else { /* Destination does not exist */ parent_of_dst = get_parent_dentry(wim, to, case_type); if (!parent_of_dst) return -errno; if (!dentry_is_directory(parent_of_dst)) return -ENOTDIR; } /* @src can't be an ancestor of @dst. Otherwise we're unlinking @src * from the tree and creating a loop... */ if (is_ancestor(src, parent_of_dst)) return -EBUSY; if (j) { if (dst) if (journaled_unlink(j, dst)) return -ENOMEM; if (journaled_unlink(j, src)) return -ENOMEM; if (journaled_change_name(j, src, path_basename(to))) return -ENOMEM; if (journaled_link(j, src, parent_of_dst)) return -ENOMEM; } else { ret = dentry_set_name(src, path_basename(to)); if (ret) return -ENOMEM; if (dst) { unlink_dentry(dst); free_dentry_tree(dst, wim->blob_table); } unlink_dentry(src); dentry_add_child(parent_of_dst, src); } if (src->d_full_path) for_dentry_in_tree(src, free_dentry_full_path, NULL); return 0; } static int execute_rename_command(struct update_command_journal *j, WIMStruct *wim, const struct wimlib_update_command *rename_cmd) { int ret; ret = rename_wim_path(wim, rename_cmd->rename.wim_source_path, rename_cmd->rename.wim_target_path, WIMLIB_CASE_PLATFORM_DEFAULT, j); if (ret) { ret = -ret; errno = ret; ERROR_WITH_ERRNO("Can't rename \"%"TS"\" to \"%"TS"\"", rename_cmd->rename.wim_source_path, rename_cmd->rename.wim_target_path); switch (ret) { case ENOMEM: ret = WIMLIB_ERR_NOMEM; break; case ENOTDIR: ret = WIMLIB_ERR_NOTDIR; break; case ENOTEMPTY: case EBUSY: /* XXX: EBUSY is returned when the rename would create a * loop. It maybe should have its own error code. */ ret = WIMLIB_ERR_NOTEMPTY; break; case EISDIR: ret = WIMLIB_ERR_IS_DIRECTORY; break; case ENOENT: default: ret = WIMLIB_ERR_PATH_DOES_NOT_EXIST; break; } } return ret; } static bool have_command_type(const struct wimlib_update_command *cmds, size_t num_cmds, enum wimlib_update_op op) { for (size_t i = 0; i < num_cmds; i++) if (cmds[i].op == op) return true; return false; } static int execute_update_commands(WIMStruct *wim, const struct wimlib_update_command *cmds, size_t num_cmds, int update_flags) { struct wim_inode_table *inode_table; struct wim_sd_set *sd_set; struct list_head unhashed_blobs; struct update_command_journal *j; union wimlib_progress_info info; int ret; if (have_command_type(cmds, num_cmds, WIMLIB_UPDATE_OP_ADD)) { /* If we have at least one "add" command, create the inode and * security descriptor tables to index new inodes and new * security descriptors, respectively. */ inode_table = alloca(sizeof(struct wim_inode_table)); sd_set = alloca(sizeof(struct wim_sd_set)); ret = init_inode_table(inode_table, 64); if (ret) goto out; ret = init_sd_set(sd_set, wim_get_current_security_data(wim)); if (ret) goto out_destroy_inode_table; INIT_LIST_HEAD(&unhashed_blobs); } else { inode_table = NULL; sd_set = NULL; } /* Start an in-memory journal to allow rollback if something goes wrong */ j = new_update_command_journal(num_cmds, &wim_get_current_image_metadata(wim)->root_dentry, wim->blob_table); if (!j) { ret = WIMLIB_ERR_NOMEM; goto out_destroy_sd_set; } info.update.completed_commands = 0; info.update.total_commands = num_cmds; ret = 0; for (size_t i = 0; i < num_cmds; i++) { info.update.command = &cmds[i]; if (update_flags & WIMLIB_UPDATE_FLAG_SEND_PROGRESS) { ret = call_progress(wim->progfunc, WIMLIB_PROGRESS_MSG_UPDATE_BEGIN_COMMAND, &info, wim->progctx); if (ret) goto rollback; } switch (cmds[i].op) { case WIMLIB_UPDATE_OP_ADD: ret = execute_add_command(j, wim, &cmds[i], inode_table, sd_set, &unhashed_blobs); break; case WIMLIB_UPDATE_OP_DELETE: ret = execute_delete_command(j, wim, &cmds[i]); break; case WIMLIB_UPDATE_OP_RENAME: ret = execute_rename_command(j, wim, &cmds[i]); break; } if (unlikely(ret)) goto rollback; info.update.completed_commands++; if (update_flags & WIMLIB_UPDATE_FLAG_SEND_PROGRESS) { ret = call_progress(wim->progfunc, WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND, &info, wim->progctx); if (ret) goto rollback; } next_command(j); } commit_update(j); if (inode_table) { struct wim_image_metadata *imd; imd = wim_get_current_image_metadata(wim); list_splice_tail(&unhashed_blobs, &imd->unhashed_blobs); inode_table_prepare_inode_list(inode_table, &imd->inode_list); } goto out_destroy_sd_set; rollback: if (sd_set) rollback_new_security_descriptors(sd_set); rollback_update(j); out_destroy_sd_set: if (sd_set) destroy_sd_set(sd_set); out_destroy_inode_table: if (inode_table) destroy_inode_table(inode_table); out: return ret; } static int check_add_command(struct wimlib_update_command *cmd, const struct wim_header *hdr) { int add_flags = cmd->add.add_flags; if (add_flags & ~(WIMLIB_ADD_FLAG_NTFS | WIMLIB_ADD_FLAG_DEREFERENCE | WIMLIB_ADD_FLAG_VERBOSE | /* BOOT doesn't make sense for wimlib_update_image(). */ /*WIMLIB_ADD_FLAG_BOOT |*/ WIMLIB_ADD_FLAG_UNIX_DATA | WIMLIB_ADD_FLAG_NO_ACLS | WIMLIB_ADD_FLAG_STRICT_ACLS | WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE | WIMLIB_ADD_FLAG_RPFIX | WIMLIB_ADD_FLAG_NORPFIX | WIMLIB_ADD_FLAG_NO_UNSUPPORTED_EXCLUDE | WIMLIB_ADD_FLAG_WINCONFIG | WIMLIB_ADD_FLAG_WIMBOOT | WIMLIB_ADD_FLAG_NO_REPLACE | WIMLIB_ADD_FLAG_TEST_FILE_EXCLUSION | WIMLIB_ADD_FLAG_SNAPSHOT | #ifdef ENABLE_TEST_SUPPORT WIMLIB_ADD_FLAG_GENERATE_TEST_DATA | #endif WIMLIB_ADD_FLAG_FILE_PATHS_UNNEEDED)) return WIMLIB_ERR_INVALID_PARAM; bool is_entire_image = WIMLIB_IS_WIM_ROOT_PATH(cmd->add.wim_target_path); #ifndef WITH_NTFS_3G if (add_flags & WIMLIB_ADD_FLAG_NTFS) { ERROR("NTFS-3G capture mode is unsupported because wimlib " "was compiled --without-ntfs-3g"); return WIMLIB_ERR_UNSUPPORTED; } #endif #ifdef __WIN32__ /* Check for flags not supported on Windows. */ if (add_flags & WIMLIB_ADD_FLAG_UNIX_DATA) { ERROR("Capturing UNIX-specific data is not supported on Windows"); return WIMLIB_ERR_UNSUPPORTED; } if (add_flags & WIMLIB_ADD_FLAG_DEREFERENCE) { ERROR("Dereferencing symbolic links is not supported on Windows"); return WIMLIB_ERR_UNSUPPORTED; } #else /* Check for flags only supported on Windows. */ /* Currently, SNAPSHOT means Windows VSS. In the future, it perhaps * could be implemented for other types of snapshots, such as btrfs. */ if (add_flags & WIMLIB_ADD_FLAG_SNAPSHOT) { ERROR("Snapshot mode is only supported on Windows, where it uses VSS."); return WIMLIB_ERR_UNSUPPORTED; } #endif /* VERBOSE implies EXCLUDE_VERBOSE */ if (add_flags & WIMLIB_ADD_FLAG_VERBOSE) add_flags |= WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE; /* Check for contradictory reparse point fixup flags */ if ((add_flags & (WIMLIB_ADD_FLAG_RPFIX | WIMLIB_ADD_FLAG_NORPFIX)) == (WIMLIB_ADD_FLAG_RPFIX | WIMLIB_ADD_FLAG_NORPFIX)) { ERROR("Cannot specify RPFIX and NORPFIX flags " "at the same time!"); return WIMLIB_ERR_INVALID_PARAM; } /* Set default behavior on reparse point fixups if requested */ if ((add_flags & (WIMLIB_ADD_FLAG_RPFIX | WIMLIB_ADD_FLAG_NORPFIX)) == 0) { /* Do reparse-point fixups by default if we are capturing an * entire image and either the header flag is set from previous * images, or if this is the first image being added. */ if (is_entire_image && ((hdr->flags & WIM_HDR_FLAG_RP_FIX) || hdr->image_count == 1)) add_flags |= WIMLIB_ADD_FLAG_RPFIX; } if (!is_entire_image) { if (add_flags & WIMLIB_ADD_FLAG_RPFIX) { ERROR("Cannot do reparse point fixups when " "not capturing a full image!"); return WIMLIB_ERR_INVALID_PARAM; } } /* We may have modified the add flags. */ cmd->add.add_flags = add_flags; return 0; } static int check_delete_command(const struct wimlib_update_command *cmd) { if (cmd->delete_.delete_flags & ~(WIMLIB_DELETE_FLAG_FORCE | WIMLIB_DELETE_FLAG_RECURSIVE)) return WIMLIB_ERR_INVALID_PARAM; return 0; } static int check_rename_command(const struct wimlib_update_command *cmd) { if (cmd->rename.rename_flags != 0) return WIMLIB_ERR_INVALID_PARAM; return 0; } static int check_update_command(struct wimlib_update_command *cmd, const struct wim_header *hdr) { switch (cmd->op) { case WIMLIB_UPDATE_OP_ADD: return check_add_command(cmd, hdr); case WIMLIB_UPDATE_OP_DELETE: return check_delete_command(cmd); case WIMLIB_UPDATE_OP_RENAME: return check_rename_command(cmd); } return 0; } static int check_update_commands(struct wimlib_update_command *cmds, size_t num_cmds, const struct wim_header *hdr) { int ret = 0; for (size_t i = 0; i < num_cmds; i++) { ret = check_update_command(&cmds[i], hdr); if (ret) break; } return ret; } static void free_update_commands(struct wimlib_update_command *cmds, size_t num_cmds) { if (cmds) { for (size_t i = 0; i < num_cmds; i++) { switch (cmds[i].op) { case WIMLIB_UPDATE_OP_ADD: FREE(cmds[i].add.wim_target_path); break; case WIMLIB_UPDATE_OP_DELETE: FREE(cmds[i].delete_.wim_path); break; case WIMLIB_UPDATE_OP_RENAME: FREE(cmds[i].rename.wim_source_path); FREE(cmds[i].rename.wim_target_path); break; } } FREE(cmds); } } static int copy_update_commands(const struct wimlib_update_command *cmds, size_t num_cmds, struct wimlib_update_command **cmds_copy_ret) { int ret; struct wimlib_update_command *cmds_copy; cmds_copy = CALLOC(num_cmds, sizeof(cmds[0])); if (!cmds_copy) goto oom; for (size_t i = 0; i < num_cmds; i++) { cmds_copy[i].op = cmds[i].op; switch (cmds[i].op) { case WIMLIB_UPDATE_OP_ADD: cmds_copy[i].add.fs_source_path = cmds[i].add.fs_source_path; cmds_copy[i].add.wim_target_path = canonicalize_wim_path(cmds[i].add.wim_target_path); if (!cmds_copy[i].add.wim_target_path) goto oom; cmds_copy[i].add.config_file = cmds[i].add.config_file; cmds_copy[i].add.add_flags = cmds[i].add.add_flags; break; case WIMLIB_UPDATE_OP_DELETE: cmds_copy[i].delete_.wim_path = canonicalize_wim_path(cmds[i].delete_.wim_path); if (!cmds_copy[i].delete_.wim_path) goto oom; cmds_copy[i].delete_.delete_flags = cmds[i].delete_.delete_flags; break; case WIMLIB_UPDATE_OP_RENAME: cmds_copy[i].rename.wim_source_path = canonicalize_wim_path(cmds[i].rename.wim_source_path); cmds_copy[i].rename.wim_target_path = canonicalize_wim_path(cmds[i].rename.wim_target_path); if (!cmds_copy[i].rename.wim_source_path || !cmds_copy[i].rename.wim_target_path) goto oom; break; default: ERROR("Unknown update operation %u", cmds[i].op); ret = WIMLIB_ERR_INVALID_PARAM; goto err; } } *cmds_copy_ret = cmds_copy; ret = 0; out: return ret; oom: ret = WIMLIB_ERR_NOMEM; err: free_update_commands(cmds_copy, num_cmds); goto out; } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_update_image(WIMStruct *wim, int image, const struct wimlib_update_command *cmds, size_t num_cmds, int update_flags) { int ret; struct wim_image_metadata *imd; struct wimlib_update_command *cmds_copy; if (update_flags & ~WIMLIB_UPDATE_FLAG_SEND_PROGRESS) return WIMLIB_ERR_INVALID_PARAM; /* Load the metadata for the image to modify (if not loaded already) */ ret = select_wim_image(wim, image); if (ret) return ret; imd = wim->image_metadata[image - 1]; /* Don't allow updating an image currently being shared by multiple * WIMStructs (as a result of an export) */ if (imd->refcnt > 1) return WIMLIB_ERR_IMAGE_HAS_MULTIPLE_REFERENCES; /* Make a copy of the update commands, in the process doing certain * canonicalizations on paths (e.g. translating backslashes to forward * slashes). This is done to avoid modifying the caller's copy of the * commands. */ ret = copy_update_commands(cmds, num_cmds, &cmds_copy); if (ret) return ret; /* Perform additional checks on the update commands before we execute * them. */ ret = check_update_commands(cmds_copy, num_cmds, &wim->hdr); if (ret) goto out_free_cmds_copy; /* Actually execute the update commands. */ ret = execute_update_commands(wim, cmds_copy, num_cmds, update_flags); if (ret) goto out_free_cmds_copy; mark_image_dirty(imd); for (size_t i = 0; i < num_cmds; i++) if (cmds_copy[i].op == WIMLIB_UPDATE_OP_ADD && cmds_copy[i].add.add_flags & WIMLIB_ADD_FLAG_RPFIX) wim->hdr.flags |= WIM_HDR_FLAG_RP_FIX; out_free_cmds_copy: free_update_commands(cmds_copy, num_cmds); return ret; } WIMLIBAPI int wimlib_delete_path(WIMStruct *wim, int image, const tchar *path, int delete_flags) { struct wimlib_update_command cmd; cmd.op = WIMLIB_UPDATE_OP_DELETE; cmd.delete_.wim_path = (tchar *)path; cmd.delete_.delete_flags = delete_flags; return wimlib_update_image(wim, image, &cmd, 1, 0); } WIMLIBAPI int wimlib_rename_path(WIMStruct *wim, int image, const tchar *source_path, const tchar *dest_path) { struct wimlib_update_command cmd; cmd.op = WIMLIB_UPDATE_OP_RENAME; cmd.rename.wim_source_path = (tchar *)source_path; cmd.rename.wim_target_path = (tchar *)dest_path; cmd.rename.rename_flags = 0; return wimlib_update_image(wim, image, &cmd, 1, 0); } WIMLIBAPI int wimlib_add_tree(WIMStruct *wim, int image, const tchar *fs_source_path, const tchar *wim_target_path, int add_flags) { struct wimlib_update_command cmd; cmd.op = WIMLIB_UPDATE_OP_ADD; cmd.add.fs_source_path = (tchar *)fs_source_path; cmd.add.wim_target_path = (tchar *)wim_target_path; cmd.add.add_flags = add_flags; cmd.add.config_file = NULL; return wimlib_update_image(wim, image, &cmd, 1, 0); } wimlib-1.13.1/src/inode.c0000644000175000017500000004076513272231267012053 00000000000000/* * inode.c * * Functions that operate on WIM inodes. * * See dentry.c for a description of the relationship between WIM dentries and * WIM inodes. */ /* * Copyright (C) 2012-2018 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include "wimlib/assert.h" #include "wimlib/blob_table.h" #include "wimlib/dentry.h" #include "wimlib/encoding.h" #include "wimlib/error.h" #include "wimlib/inode.h" #include "wimlib/timestamp.h" /* * The 'stream_name' field of unnamed streams always points to this array, which * is an empty UTF-16 string. */ const utf16lechar NO_STREAM_NAME[1]; /* Allocate a new inode and associate the specified dentry with it. */ struct wim_inode * new_inode(struct wim_dentry *dentry, bool set_timestamps) { struct wim_inode *inode; inode = CALLOC(1, sizeof(struct wim_inode)); if (!inode) return NULL; inode->i_security_id = -1; /*inode->i_nlink = 0;*/ inode->i_rp_flags = WIM_RP_FLAG_NOT_FIXED; INIT_HLIST_HEAD(&inode->i_alias_list); inode->i_streams = inode->i_embedded_streams; if (set_timestamps) { u64 now = now_as_wim_timestamp(); inode->i_creation_time = now; inode->i_last_access_time = now; inode->i_last_write_time = now; } d_associate(dentry, inode); return inode; } static inline void destroy_stream(struct wim_inode_stream *strm) { if (strm->stream_name != NO_STREAM_NAME) FREE(strm->stream_name); } static void free_inode(struct wim_inode *inode) { for (unsigned i = 0; i < inode->i_num_streams; i++) destroy_stream(&inode->i_streams[i]); if (inode->i_streams != inode->i_embedded_streams) FREE(inode->i_streams); if (inode->i_extra) FREE(inode->i_extra); if (!hlist_unhashed(&inode->i_hlist_node)) hlist_del(&inode->i_hlist_node); FREE(inode); } static inline void free_inode_if_unneeded(struct wim_inode *inode) { if (inode->i_nlink) return; #ifdef WITH_FUSE if (inode->i_num_opened_fds) return; #endif free_inode(inode); } /* Associate a dentry with the specified inode. */ void d_associate(struct wim_dentry *dentry, struct wim_inode *inode) { wimlib_assert(!dentry->d_inode); hlist_add_head(&dentry->d_alias_node, &inode->i_alias_list); dentry->d_inode = inode; inode->i_nlink++; } /* Disassociate a dentry from its inode, if any. Following this, free the inode * if it is no longer in use. */ void d_disassociate(struct wim_dentry *dentry) { struct wim_inode *inode = dentry->d_inode; if (unlikely(!inode)) return; wimlib_assert(inode->i_nlink > 0); hlist_del(&dentry->d_alias_node); dentry->d_inode = NULL; inode->i_nlink--; free_inode_if_unneeded(inode); } #ifdef WITH_FUSE void inode_dec_num_opened_fds(struct wim_inode *inode) { wimlib_assert(inode->i_num_opened_fds > 0); if (--inode->i_num_opened_fds == 0) { /* The last file descriptor to this inode was closed. */ FREE(inode->i_fds); inode->i_fds = NULL; inode->i_num_allocated_fds = 0; free_inode_if_unneeded(inode); } } #endif /* * Retrieve a stream of an inode. * * @inode * The inode from which the stream is desired * @stream_type * The type of the stream desired * @stream_name * The name of the stream desired as a null-terminated UTF-16LE string, or * NO_STREAM_NAME if an unnamed stream is desired * * Returns a pointer to the stream if found, otherwise NULL. */ struct wim_inode_stream * inode_get_stream(const struct wim_inode *inode, int stream_type, const utf16lechar *stream_name) { if (stream_name == NO_STREAM_NAME) /* Optimization */ return inode_get_unnamed_stream(inode, stream_type); for (unsigned i = 0; i < inode->i_num_streams; i++) { struct wim_inode_stream *strm = &inode->i_streams[i]; if (strm->stream_type == stream_type && !cmp_utf16le_strings_z(strm->stream_name, stream_name, default_ignore_case)) { return strm; } } return NULL; } /* * This is equivalent to inode_get_stream(inode, stream_type, NO_STREAM_NAME), * but this optimizes for the unnamed case by not doing full string comparisons. */ struct wim_inode_stream * inode_get_unnamed_stream(const struct wim_inode *inode, int stream_type) { for (unsigned i = 0; i < inode->i_num_streams; i++) { struct wim_inode_stream *strm = &inode->i_streams[i]; if (strm->stream_type == stream_type && strm->stream_name == NO_STREAM_NAME) { return strm; } } return NULL; } static void inode_set_stream_blob(struct wim_inode *inode, struct wim_inode_stream *strm, struct blob_descriptor *new_blob) { strm->_stream_blob = new_blob; strm->stream_resolved = 1; if (new_blob) new_blob->refcnt += inode->i_nlink; } static void inode_unset_stream_blob(struct wim_inode *inode, struct wim_inode_stream *strm, struct blob_table *blob_table) { struct blob_descriptor *old_blob; old_blob = stream_blob(strm, blob_table); if (old_blob) blob_subtract_refcnt(old_blob, blob_table, inode->i_nlink); strm->_stream_blob = NULL; strm->stream_resolved = 1; } /* * Replace the blob associated with the specified stream. * * @inode * The inode containing @strm * @strm * The stream whose data needs to be replaced * @new_blob * The new blob descriptor to assign * @blob_table * Pointer to the blob table in which data blobs are being indexed */ void inode_replace_stream_blob(struct wim_inode *inode, struct wim_inode_stream *strm, struct blob_descriptor *new_blob, struct blob_table *blob_table) { inode_unset_stream_blob(inode, strm, blob_table); inode_set_stream_blob(inode, strm, new_blob); } /* * Add a new stream to the specified inode. * * @inode * The inode to which to add the stream * @stream_type * The type of the stream being added * @stream_name * The name of the stream being added as a null-terminated UTF-16LE string, * or NO_STREAM_NAME if the stream is unnamed * @blob * The blob that the new stream will initially reference, or NULL * * Returns a pointer to the new stream, or NULL with errno set if it could not * be added. */ struct wim_inode_stream * inode_add_stream(struct wim_inode *inode, int stream_type, const utf16lechar *stream_name, struct blob_descriptor *blob) { if (inode->i_num_streams >= 0xFFFF) { ERROR("Inode has too many streams! Path=\"%"TS"\"", inode_any_full_path(inode)); errno = EFBIG; return NULL; } struct wim_inode_stream *streams; struct wim_inode_stream *new_strm; if (inode->i_streams == inode->i_embedded_streams) { if (inode->i_num_streams < ARRAY_LEN(inode->i_embedded_streams)) { streams = inode->i_embedded_streams; } else { streams = MALLOC((inode->i_num_streams + 1) * sizeof(inode->i_streams[0])); if (!streams) return NULL; memcpy(streams, inode->i_streams, (inode->i_num_streams * sizeof(inode->i_streams[0]))); inode->i_streams = streams; } } else { streams = REALLOC(inode->i_streams, (inode->i_num_streams + 1) * sizeof(inode->i_streams[0])); if (!streams) return NULL; inode->i_streams = streams; } new_strm = &streams[inode->i_num_streams]; memset(new_strm, 0, sizeof(*new_strm)); new_strm->stream_type = stream_type; if (!*stream_name) { /* Unnamed stream */ new_strm->stream_name = (utf16lechar *)NO_STREAM_NAME; } else { /* Named stream */ new_strm->stream_name = utf16le_dup(stream_name); if (!new_strm->stream_name) return NULL; } new_strm->stream_id = inode->i_next_stream_id++; inode_set_stream_blob(inode, new_strm, blob); inode->i_num_streams++; return new_strm; } /* * Replace the data of the specified stream. * * @inode * The inode containing @strm * @strm * The stream whose data needs to be replaced * @data * The buffer of data to assign to the stream * @size * Size of the @data buffer, in bytes * @blob_table * Pointer to the blob table in which data blobs are being indexed * * Returns true if successful; false with errno set if unsuccessful. */ bool inode_replace_stream_data(struct wim_inode *inode, struct wim_inode_stream *strm, const void *data, size_t size, struct blob_table *blob_table) { struct blob_descriptor *new_blob = NULL; if (size) { new_blob = new_blob_from_data_buffer(data, size, blob_table); if (!new_blob) return false; } inode_replace_stream_blob(inode, strm, new_blob, blob_table); return true; } /* * Add a new stream to the specified inode and assign it the specified data. * * @inode * The inode to which to add the stream * @stream_type * The type of the stream being added * @stream_name * The name of the stream being added as a null-terminated UTF-16LE string, * or NO_STREAM_NAME if the stream is unnamed * @data * The buffer of data to assign to the new stream * @size * Size of the @data buffer, in bytes * @blob_table * Pointer to the blob table in which data blobs are being indexed * * Returns true if successful; false with errno set if unsuccessful. */ bool inode_add_stream_with_data(struct wim_inode *inode, int stream_type, const utf16lechar *stream_name, const void *data, size_t size, struct blob_table *blob_table) { struct wim_inode_stream *strm; struct blob_descriptor *blob = NULL; strm = inode_add_stream(inode, stream_type, stream_name, NULL); if (!strm) return false; if (size) { blob = new_blob_from_data_buffer(data, size, blob_table); if (unlikely(!blob)) { inode_remove_stream(inode, strm, blob_table); return false; } } inode_set_stream_blob(inode, strm, blob); return true; } /* * Remove a stream from the specified inode. * * This handles releasing the references to the blob descriptor, if any. */ void inode_remove_stream(struct wim_inode *inode, struct wim_inode_stream *strm, struct blob_table *blob_table) { unsigned idx = strm - inode->i_streams; wimlib_assert(idx < inode->i_num_streams); inode_unset_stream_blob(inode, strm, blob_table); destroy_stream(strm); memmove(strm, strm + 1, (inode->i_num_streams - idx - 1) * sizeof(inode->i_streams[0])); inode->i_num_streams--; } /* Returns true iff the specified inode has at least one named data stream. */ bool inode_has_named_data_stream(const struct wim_inode *inode) { for (unsigned i = 0; i < inode->i_num_streams; i++) if (stream_is_named_data_stream(&inode->i_streams[i])) return true; return false; } /* * Resolve an inode's streams. * * For each stream, this replaces the SHA-1 message digest of the blob data with * a pointer to the 'struct blob_descriptor' for the blob. Blob descriptors are * looked up in @table. * * If @force is %false: * If any of the needed blobs do not exist in @table, return * WIMLIB_ERR_RESOURCE_NOT_FOUND. * If @force is %true: * If any of the needed blobs do not exist in @table, allocate new blob * descriptors for them and insert them into @table. This does not, of * course, cause the data of these blobs to magically exist, but this is * needed by the code for extraction from a pipe. * * Returns 0 on success; WIMLIB_ERR_NOMEM if out of memory; or * WIMLIB_ERR_RESOURCE_NOT_FOUND if @force is %false and at least one blob * referenced by the inode was missing. */ int inode_resolve_streams(struct wim_inode *inode, struct blob_table *table, bool force) { for (unsigned i = 0; i < inode->i_num_streams; i++) { struct wim_inode_stream *strm = &inode->i_streams[i]; if (strm->stream_resolved) continue; const u8 *hash = stream_hash(strm); struct blob_descriptor *blob = NULL; if (!is_zero_hash(hash)) { blob = lookup_blob(table, hash); if (!blob) { if (!force) return blob_not_found_error(inode, hash); blob = new_blob_descriptor(); if (!blob) return WIMLIB_ERR_NOMEM; copy_hash(blob->hash, hash); blob_table_insert(table, blob); } } strm->_stream_blob = blob; strm->stream_resolved = 1; } return 0; } int blob_not_found_error(const struct wim_inode *inode, const u8 *hash) { if (wimlib_print_errors) { tchar hashstr[SHA1_HASH_SIZE * 2 + 1]; sprint_hash(hash, hashstr); ERROR("\"%"TS"\": blob not found\n" " SHA-1 message digest of missing blob:\n" " %"TS"", inode_any_full_path(inode), hashstr); } return WIMLIB_ERR_RESOURCE_NOT_FOUND; } /* * Return the blob descriptor for the specified stream, or NULL if the stream is * empty or its blob is not available in @table. */ struct blob_descriptor * stream_blob(const struct wim_inode_stream *strm, const struct blob_table *table) { if (strm->stream_resolved) return strm->_stream_blob; else return lookup_blob(table, strm->_stream_hash); } /* * Return the SHA-1 message digest of the data of the specified stream, or a * void SHA-1 of all zeroes if the specified stream is empty, or NULL if the * specified stream is unhashed. (Most callers ensure the stream cannot be * unhashed.) */ const u8 * stream_hash(const struct wim_inode_stream *strm) { if (!strm->stream_resolved) return strm->_stream_hash; if (!strm->_stream_blob) return zero_hash; if (strm->_stream_blob->unhashed) return NULL; return strm->_stream_blob->hash; } /* * Return the blob descriptor for the unnamed data stream of the inode, or NULL * if the inode does not have an unnamed data stream, the inode's unnamed data * stream is empty, or the blob for the inode's unnamed data stream is not * available in @blob_table. */ struct blob_descriptor * inode_get_blob_for_unnamed_data_stream(const struct wim_inode *inode, const struct blob_table *blob_table) { const struct wim_inode_stream *strm; strm = inode_get_unnamed_data_stream(inode); if (!strm) return NULL; return stream_blob(strm, blob_table); } /* Like inode_get_blob_for_unnamed_data_stream(), but assumes the unnamed data * stream is resolved. */ struct blob_descriptor * inode_get_blob_for_unnamed_data_stream_resolved(const struct wim_inode *inode) { const struct wim_inode_stream *strm; strm = inode_get_unnamed_data_stream(inode); if (!strm) return NULL; return stream_blob_resolved(strm); } /* * Return the SHA-1 message digest of the unnamed data stream of the inode, or a * void SHA-1 of all zeroes if the inode does not have an unnamed data stream or * if the inode's unnamed data stream is empty, or NULL if the inode's unnamed * data stream is unhashed. (Most callers ensure the stream cannot be * unhashed.) */ const u8 * inode_get_hash_of_unnamed_data_stream(const struct wim_inode *inode) { const struct wim_inode_stream *strm; strm = inode_get_unnamed_data_stream(inode); if (!strm) return zero_hash; return stream_hash(strm); } /* Acquire another reference to each blob referenced by this inode. This is * necessary when creating a hard link to this inode. * * All streams of the inode must be resolved. */ void inode_ref_blobs(struct wim_inode *inode) { for (unsigned i = 0; i < inode->i_num_streams; i++) { struct blob_descriptor *blob; blob = stream_blob_resolved(&inode->i_streams[i]); if (blob) blob->refcnt++; } } /* Release a reference to each blob referenced by this inode. This is necessary * when deleting a hard link to this inode. */ void inode_unref_blobs(struct wim_inode *inode, struct blob_table *blob_table) { for (unsigned i = 0; i < inode->i_num_streams; i++) { struct blob_descriptor *blob; blob = stream_blob(&inode->i_streams[i], blob_table); if (blob) blob_decrement_refcnt(blob, blob_table); } } /* * Given a blob descriptor, return a pointer to the pointer contained in the * stream that references it. * * This is only possible for "unhashed" blobs, which are guaranteed to have only * one referencing stream, and that reference is guaranteed to be in a resolved * stream. (It can't be in an unresolved stream, since that would imply the * hash is known!) */ struct blob_descriptor ** retrieve_pointer_to_unhashed_blob(struct blob_descriptor *blob) { wimlib_assert(blob->unhashed); struct wim_inode *inode = blob->back_inode; for (unsigned i = 0; i < inode->i_num_streams; i++) { if (inode->i_streams[i].stream_id == blob->back_stream_id) { wimlib_assert(inode->i_streams[i]._stream_blob == blob); return &inode->i_streams[i]._stream_blob; } } wimlib_assert(0); return NULL; } wimlib-1.13.1/src/compress_serial.c0000644000175000017500000000725013160354224014132 00000000000000/* * compress_serial.c * * Compress chunks of data (serial version). */ /* * Copyright (C) 2013 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include "wimlib.h" #include "wimlib/assert.h" #include "wimlib/chunk_compressor.h" #include "wimlib/util.h" struct serial_chunk_compressor { struct chunk_compressor base; struct wimlib_compressor *compressor; u8 *udata; u8 *cdata; u32 usize; u8 *result_data; u32 result_size; }; static void serial_chunk_compressor_destroy(struct chunk_compressor *_ctx) { struct serial_chunk_compressor *ctx = (struct serial_chunk_compressor*)_ctx; if (ctx == NULL) return; wimlib_free_compressor(ctx->compressor); FREE(ctx->udata); FREE(ctx->cdata); FREE(ctx); } static void * serial_chunk_compressor_get_chunk_buffer(struct chunk_compressor *_ctx) { struct serial_chunk_compressor *ctx = (struct serial_chunk_compressor*)_ctx; if (ctx->result_data) return NULL; return ctx->udata; } static void serial_chunk_compressor_signal_chunk_filled(struct chunk_compressor *_ctx, u32 usize) { struct serial_chunk_compressor *ctx = (struct serial_chunk_compressor*)_ctx; u32 csize; wimlib_assert(usize > 0); wimlib_assert(usize <= ctx->base.out_chunk_size); ctx->usize = usize; csize = wimlib_compress(ctx->udata, usize, ctx->cdata, usize - 1, ctx->compressor); if (csize) { ctx->result_data = ctx->cdata; ctx->result_size = csize; } else { ctx->result_data = ctx->udata; ctx->result_size = ctx->usize; } } static bool serial_chunk_compressor_get_compression_result(struct chunk_compressor *_ctx, const void **cdata_ret, u32 *csize_ret, u32 *usize_ret) { struct serial_chunk_compressor *ctx = (struct serial_chunk_compressor *)_ctx; if (!ctx->result_data) return false; *cdata_ret = ctx->result_data; *csize_ret = ctx->result_size; *usize_ret = ctx->usize; ctx->result_data = NULL; return true; } int new_serial_chunk_compressor(int out_ctype, u32 out_chunk_size, struct chunk_compressor **compressor_ret) { struct serial_chunk_compressor *ctx; int ret; wimlib_assert(out_chunk_size > 0); ctx = CALLOC(1, sizeof(*ctx)); if (ctx == NULL) return WIMLIB_ERR_NOMEM; ctx->base.out_ctype = out_ctype; ctx->base.out_chunk_size = out_chunk_size; ctx->base.num_threads = 1; ctx->base.destroy = serial_chunk_compressor_destroy; ctx->base.get_chunk_buffer = serial_chunk_compressor_get_chunk_buffer; ctx->base.signal_chunk_filled = serial_chunk_compressor_signal_chunk_filled; ctx->base.get_compression_result = serial_chunk_compressor_get_compression_result; ret = wimlib_create_compressor(out_ctype, out_chunk_size, WIMLIB_COMPRESSOR_FLAG_DESTRUCTIVE, &ctx->compressor); if (ret) goto err; ctx->udata = MALLOC(out_chunk_size); ctx->cdata = MALLOC(out_chunk_size - 1); if (ctx->udata == NULL || ctx->cdata == NULL) { ret = WIMLIB_ERR_NOMEM; goto err; } ctx->result_data = NULL; *compressor_ret = &ctx->base; return 0; err: serial_chunk_compressor_destroy(&ctx->base); return ret; } wimlib-1.13.1/src/unix_apply.c0000644000175000017500000006507613376102673013152 00000000000000/* * unix_apply.c - Code to apply files from a WIM image on UNIX. */ /* * Copyright (C) 2012-2018 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #ifdef HAVE_SYS_XATTR_H # include #endif #include #include "wimlib/apply.h" #include "wimlib/assert.h" #include "wimlib/blob_table.h" #include "wimlib/dentry.h" #include "wimlib/error.h" #include "wimlib/file_io.h" #include "wimlib/reparse.h" #include "wimlib/timestamp.h" #include "wimlib/unix_data.h" #include "wimlib/xattr.h" /* We don't require O_NOFOLLOW, but the advantage of having it is that if we * need to extract a file to a location at which there exists a symbolic link, * open(..., O_NOFOLLOW | ...) recognizes the symbolic link rather than * following it and creating the file somewhere else. (Equivalent to * FILE_OPEN_REPARSE_POINT on Windows.) */ #ifndef O_NOFOLLOW # define O_NOFOLLOW 0 #endif static int unix_get_supported_features(const char *target, struct wim_features *supported_features) { supported_features->sparse_files = 1; supported_features->hard_links = 1; supported_features->symlink_reparse_points = 1; supported_features->unix_data = 1; supported_features->timestamps = 1; supported_features->case_sensitive_filenames = 1; #ifdef HAVE_LINUX_XATTR_SUPPORT supported_features->xattrs = 1; #endif return 0; } #define NUM_PATHBUFS 2 /* We need 2 when creating hard links */ struct unix_apply_ctx { /* Extract flags, the pointer to the WIMStruct, etc. */ struct apply_ctx common; /* Buffers for building extraction paths (allocated). */ char *pathbufs[NUM_PATHBUFS]; /* Index of next pathbuf to use */ unsigned which_pathbuf; /* Currently open file descriptors for extraction */ struct filedes open_fds[MAX_OPEN_FILES]; /* Number of currently open file descriptors in open_fds, starting from * the beginning of the array. */ unsigned num_open_fds; /* For each currently open file, whether we're writing to it in "sparse" * mode or not. */ bool is_sparse_file[MAX_OPEN_FILES]; /* Whether is_sparse_file[] is true for any currently open file */ bool any_sparse_files; /* Buffer for reading reparse point data into memory */ u8 reparse_data[REPARSE_DATA_MAX_SIZE]; /* Pointer to the next byte in @reparse_data to fill */ u8 *reparse_ptr; /* Absolute path to the target directory (allocated buffer). Only set * if needed for absolute symbolic link fixups. */ char *target_abspath; /* Number of characters in target_abspath. */ size_t target_abspath_nchars; /* Number of special files we couldn't create due to EPERM */ unsigned long num_special_files_ignored; }; /* Returns the number of characters needed to represent the path to the * specified @dentry when extracted, not including the null terminator or the * path to the target directory itself. */ static size_t unix_dentry_path_length(const struct wim_dentry *dentry) { size_t len = 0; const struct wim_dentry *d; d = dentry; do { len += d->d_extraction_name_nchars + 1; d = d->d_parent; } while (!dentry_is_root(d) && will_extract_dentry(d)); return len; } /* Returns the maximum number of characters needed to represent the path to any * dentry in @dentry_list when extracted, including the null terminator and the * path to the target directory itself. */ static size_t unix_compute_path_max(const struct list_head *dentry_list, const struct unix_apply_ctx *ctx) { size_t max = 0; size_t len; const struct wim_dentry *dentry; list_for_each_entry(dentry, dentry_list, d_extraction_list_node) { len = unix_dentry_path_length(dentry); if (len > max) max = len; } /* Account for target and null terminator. */ return ctx->common.target_nchars + max + 1; } /* Builds and returns the filesystem path to which to extract @dentry. * This cycles through NUM_PATHBUFS different buffers. */ static const char * unix_build_extraction_path(const struct wim_dentry *dentry, struct unix_apply_ctx *ctx) { char *pathbuf; char *p; const struct wim_dentry *d; pathbuf = ctx->pathbufs[ctx->which_pathbuf]; ctx->which_pathbuf = (ctx->which_pathbuf + 1) % NUM_PATHBUFS; p = &pathbuf[ctx->common.target_nchars + unix_dentry_path_length(dentry)]; *p = '\0'; d = dentry; do { p -= d->d_extraction_name_nchars; if (d->d_extraction_name_nchars) memcpy(p, d->d_extraction_name, d->d_extraction_name_nchars); *--p = '/'; d = d->d_parent; } while (!dentry_is_root(d) && will_extract_dentry(d)); return pathbuf; } /* This causes the next call to unix_build_extraction_path() to use the same * path buffer as the previous call. */ static void unix_reuse_pathbuf(struct unix_apply_ctx *ctx) { ctx->which_pathbuf = (ctx->which_pathbuf - 1) % NUM_PATHBUFS; } /* Builds and returns the filesystem path to which to extract an unspecified * alias of the @inode. This cycles through NUM_PATHBUFS different buffers. */ static const char * unix_build_inode_extraction_path(const struct wim_inode *inode, struct unix_apply_ctx *ctx) { return unix_build_extraction_path(inode_first_extraction_dentry(inode), ctx); } /* Should the specified file be extracted as a directory on UNIX? We extract * the file as a directory if FILE_ATTRIBUTE_DIRECTORY is set and the file does * not have a symlink or junction reparse point. It *may* have a different type * of reparse point. */ static inline bool should_extract_as_directory(const struct wim_inode *inode) { return (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) && !inode_is_symlink(inode); } /* Sets the timestamps on a file being extracted. * * Either @fd or @path must be specified (not -1 and not NULL, respectively). */ static int unix_set_timestamps(int fd, const char *path, u64 atime, u64 mtime) { { struct timespec times[2]; times[0] = wim_timestamp_to_timespec(atime); times[1] = wim_timestamp_to_timespec(mtime); errno = ENOSYS; #ifdef HAVE_FUTIMENS if (fd >= 0 && !futimens(fd, times)) return 0; #endif #ifdef HAVE_UTIMENSAT if (fd < 0 && !utimensat(AT_FDCWD, path, times, AT_SYMLINK_NOFOLLOW)) return 0; #endif if (errno != ENOSYS) return WIMLIB_ERR_SET_TIMESTAMPS; } { struct timeval times[2]; times[0] = wim_timestamp_to_timeval(atime); times[1] = wim_timestamp_to_timeval(mtime); if (fd >= 0 && !futimes(fd, times)) return 0; if (fd < 0 && !lutimes(path, times)) return 0; return WIMLIB_ERR_SET_TIMESTAMPS; } } static int unix_set_owner_and_group(int fd, const char *path, uid_t uid, gid_t gid) { if (fd >= 0 && !fchown(fd, uid, gid)) return 0; if (fd < 0 && !lchown(path, uid, gid)) return 0; return WIMLIB_ERR_SET_SECURITY; } static int unix_set_mode(int fd, const char *path, mode_t mode) { if (fd >= 0 && !fchmod(fd, mode)) return 0; if (fd < 0 && !chmod(path, mode)) return 0; return WIMLIB_ERR_SET_SECURITY; } #ifdef HAVE_LINUX_XATTR_SUPPORT /* Apply extended attributes to a file */ static int apply_linux_xattrs(int fd, const struct wim_inode *inode, const char *path, struct unix_apply_ctx *ctx, const void *entries, size_t entries_size, bool is_old_format) { const void * const entries_end = entries + entries_size; char name[WIM_XATTR_NAME_MAX + 1]; for (const void *entry = entries; entry < entries_end; entry = is_old_format ? (const void *)old_xattr_entry_next(entry) : (const void *)xattr_entry_next(entry)) { bool valid; u16 name_len; const void *value; u32 value_len; int res; if (is_old_format) { valid = old_valid_xattr_entry(entry, entries_end - entry); } else { valid = valid_xattr_entry(entry, entries_end - entry); } if (!valid) { if (!path) { path = unix_build_inode_extraction_path(inode, ctx); } ERROR("\"%s\": extended attribute is corrupt or unsupported", path); return WIMLIB_ERR_INVALID_XATTR; } if (is_old_format) { const struct wimlib_xattr_entry_old *e = entry; name_len = le16_to_cpu(e->name_len); memcpy(name, e->name, name_len); value = e->name + name_len; value_len = le32_to_cpu(e->value_len); } else { const struct wim_xattr_entry *e = entry; name_len = e->name_len; memcpy(name, e->name, name_len); value = e->name + name_len + 1; value_len = le16_to_cpu(e->value_len); } name[name_len] = '\0'; if (fd >= 0) res = fsetxattr(fd, name, value, value_len, 0); else res = lsetxattr(path, name, value, value_len, 0); if (unlikely(res != 0)) { if (!path) { path = unix_build_inode_extraction_path(inode, ctx); } if (is_linux_security_xattr(name) && (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS)) { ERROR_WITH_ERRNO("\"%s\": unable to set extended attribute \"%s\"", path, name); return WIMLIB_ERR_SET_XATTR; } WARNING_WITH_ERRNO("\"%s\": unable to set extended attribute \"%s\"", path, name); } } return 0; } #endif /* HAVE_LINUX_XATTR_SUPPORT */ /* * Apply UNIX-specific metadata to a file if available. This includes standard * UNIX permissions (uid, gid, and mode) and possibly extended attributes too. * * Note that some xattrs which grant privileges, e.g. security.capability, are * cleared by Linux on chown(), even when running as root. Also, when running * as non-root, if we need to chmod() the file to readonly, we can't do that * before setting xattrs because setxattr() requires write permission. These * restrictions result in the following ordering which we follow: chown(), * setxattr(), then chmod(). * * N.B. the file may be specified by either 'fd' (for regular files) or 'path', * and it may be a symlink. For symlinks we need lchown() and lsetxattr() but * need to skip the chmod(), since mode bits are not meaningful for symlinks. */ static int apply_unix_metadata(int fd, const struct wim_inode *inode, const char *path, struct unix_apply_ctx *ctx) { bool have_dat; struct wimlib_unix_data dat; #ifdef HAVE_LINUX_XATTR_SUPPORT const void *entries; u32 entries_size; bool is_old_format; #endif int ret; have_dat = inode_get_unix_data(inode, &dat); if (have_dat) { ret = unix_set_owner_and_group(fd, path, dat.uid, dat.gid); if (ret) { if (!path) path = unix_build_inode_extraction_path(inode, ctx); if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS) { ERROR_WITH_ERRNO("\"%s\": unable to set uid=%"PRIu32" and gid=%"PRIu32, path, dat.uid, dat.gid); return ret; } WARNING_WITH_ERRNO("\"%s\": unable to set uid=%"PRIu32" and gid=%"PRIu32, path, dat.uid, dat.gid); } } #ifdef HAVE_LINUX_XATTR_SUPPORT entries = inode_get_linux_xattrs(inode, &entries_size, &is_old_format); if (entries) { ret = apply_linux_xattrs(fd, inode, path, ctx, entries, entries_size, is_old_format); if (ret) return ret; } #endif if (have_dat && !inode_is_symlink(inode)) { ret = unix_set_mode(fd, path, dat.mode); if (ret) { if (!path) path = unix_build_inode_extraction_path(inode, ctx); if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS) { ERROR_WITH_ERRNO("\"%s\": unable to set mode=0%"PRIo32, path, dat.mode); return ret; } WARNING_WITH_ERRNO("\"%s\": unable to set mode=0%"PRIo32, path, dat.mode); } } return 0; } /* * Set metadata on an extracted file. * * @fd is an open file descriptor to the extracted file, or -1. @path is the * path to the extracted file, or NULL. If valid, this function uses @fd. * Otherwise, if valid, it uses @path. Otherwise, it calculates the path to one * alias of the extracted file and uses it. */ static int unix_set_metadata(int fd, const struct wim_inode *inode, const char *path, struct unix_apply_ctx *ctx) { int ret; if (fd < 0 && !path) path = unix_build_inode_extraction_path(inode, ctx); if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) { ret = apply_unix_metadata(fd, inode, path, ctx); if (ret) return ret; } ret = unix_set_timestamps(fd, path, inode->i_last_access_time, inode->i_last_write_time); if (ret) { if (!path) path = unix_build_inode_extraction_path(inode, ctx); if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_TIMESTAMPS) { ERROR_WITH_ERRNO("\"%s\": unable to set timestamps", path); return ret; } WARNING_WITH_ERRNO("\"%s\": unable to set timestamps", path); } return 0; } /* Extract all needed aliases of the @inode, where one alias, corresponding to * @first_dentry, has already been extracted to @first_path. */ static int unix_create_hardlinks(const struct wim_inode *inode, const struct wim_dentry *first_dentry, const char *first_path, struct unix_apply_ctx *ctx) { const struct wim_dentry *dentry; const char *newpath; inode_for_each_extraction_alias(dentry, inode) { if (dentry == first_dentry) continue; newpath = unix_build_extraction_path(dentry, ctx); retry_link: if (link(first_path, newpath)) { if (errno == EEXIST && !unlink(newpath)) goto retry_link; ERROR_WITH_ERRNO("Can't create hard link " "\"%s\" => \"%s\"", newpath, first_path); return WIMLIB_ERR_LINK; } unix_reuse_pathbuf(ctx); } return 0; } /* If @dentry represents a directory, create it. */ static int unix_create_if_directory(const struct wim_dentry *dentry, struct unix_apply_ctx *ctx) { const char *path; struct stat stbuf; if (!should_extract_as_directory(dentry->d_inode)) return 0; path = unix_build_extraction_path(dentry, ctx); if (mkdir(path, 0755) && /* It's okay if the path already exists, as long as it's a * directory. */ !(errno == EEXIST && !lstat(path, &stbuf) && S_ISDIR(stbuf.st_mode))) { ERROR_WITH_ERRNO("Can't create directory \"%s\"", path); return WIMLIB_ERR_MKDIR; } return report_file_created(&ctx->common); } /* If @dentry represents an empty regular file or a special file, create it, set * its metadata, and create any needed hard links. */ static int unix_extract_if_empty_file(const struct wim_dentry *dentry, struct unix_apply_ctx *ctx) { const struct wim_inode *inode; struct wimlib_unix_data unix_data; const char *path; int ret; inode = dentry->d_inode; /* Extract all aliases only when the "first" comes up. */ if (dentry != inode_first_extraction_dentry(inode)) return 0; /* Is this a directory, a symbolic link, or any type of nonempty file? */ if (should_extract_as_directory(inode) || inode_is_symlink(inode) || inode_get_blob_for_unnamed_data_stream_resolved(inode)) return 0; /* Recognize special files in UNIX_DATA mode */ if ((ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) && inode_get_unix_data(inode, &unix_data) && !S_ISREG(unix_data.mode)) { path = unix_build_extraction_path(dentry, ctx); retry_mknod: if (mknod(path, unix_data.mode, unix_data.rdev)) { if (errno == EPERM) { WARNING_WITH_ERRNO("Can't create special " "file \"%s\"", path); ctx->num_special_files_ignored++; return 0; } if (errno == EEXIST && !unlink(path)) goto retry_mknod; ERROR_WITH_ERRNO("Can't create special file \"%s\"", path); return WIMLIB_ERR_MKNOD; } /* On special files, we can set timestamps immediately because * we don't need to write any data to them. */ ret = unix_set_metadata(-1, inode, path, ctx); } else { int fd; path = unix_build_extraction_path(dentry, ctx); retry_create: fd = open(path, O_EXCL | O_CREAT | O_WRONLY | O_NOFOLLOW, 0644); if (fd < 0) { if (errno == EEXIST && !unlink(path)) goto retry_create; ERROR_WITH_ERRNO("Can't create regular file \"%s\"", path); return WIMLIB_ERR_OPEN; } /* On empty files, we can set timestamps immediately because we * don't need to write any data to them. */ ret = unix_set_metadata(fd, inode, path, ctx); if (close(fd) && !ret) { ERROR_WITH_ERRNO("Error closing \"%s\"", path); ret = WIMLIB_ERR_WRITE; } } if (ret) return ret; ret = unix_create_hardlinks(inode, dentry, path, ctx); if (ret) return ret; return report_file_created(&ctx->common); } static int unix_create_dirs_and_empty_files(const struct list_head *dentry_list, struct unix_apply_ctx *ctx) { const struct wim_dentry *dentry; int ret; list_for_each_entry(dentry, dentry_list, d_extraction_list_node) { ret = unix_create_if_directory(dentry, ctx); if (ret) return ret; } list_for_each_entry(dentry, dentry_list, d_extraction_list_node) { ret = unix_extract_if_empty_file(dentry, ctx); if (ret) return ret; } return 0; } static void unix_count_dentries(const struct list_head *dentry_list, u64 *dir_count_ret, u64 *empty_file_count_ret) { const struct wim_dentry *dentry; u64 dir_count = 0; u64 empty_file_count = 0; list_for_each_entry(dentry, dentry_list, d_extraction_list_node) { const struct wim_inode *inode = dentry->d_inode; if (should_extract_as_directory(inode)) dir_count++; else if ((dentry == inode_first_extraction_dentry(inode)) && !inode_is_symlink(inode) && !inode_get_blob_for_unnamed_data_stream_resolved(inode)) empty_file_count++; } *dir_count_ret = dir_count; *empty_file_count_ret = empty_file_count; } static int unix_create_symlink(const struct wim_inode *inode, const char *path, size_t rpdatalen, struct unix_apply_ctx *ctx) { char target[REPARSE_POINT_MAX_SIZE]; struct blob_descriptor blob_override; int ret; blob_set_is_located_in_attached_buffer(&blob_override, ctx->reparse_data, rpdatalen); ret = wim_inode_readlink(inode, target, sizeof(target) - 1, &blob_override, ctx->target_abspath, ctx->target_abspath_nchars); if (unlikely(ret < 0)) { errno = -ret; return WIMLIB_ERR_READLINK; } target[ret] = '\0'; retry_symlink: if (symlink(target, path)) { if (errno == EEXIST && !unlink(path)) goto retry_symlink; return WIMLIB_ERR_LINK; } return 0; } static void unix_cleanup_open_fds(struct unix_apply_ctx *ctx, unsigned offset) { for (unsigned i = offset; i < ctx->num_open_fds; i++) filedes_close(&ctx->open_fds[i]); ctx->num_open_fds = 0; ctx->any_sparse_files = false; } static int unix_begin_extract_blob_instance(const struct blob_descriptor *blob, const struct wim_inode *inode, const struct wim_inode_stream *strm, struct unix_apply_ctx *ctx) { const struct wim_dentry *first_dentry; const char *first_path; int fd; if (unlikely(strm->stream_type == STREAM_TYPE_REPARSE_POINT)) { /* On UNIX, symbolic links must be created with symlink(), which * requires that the full link target be available. */ if (blob->size > REPARSE_DATA_MAX_SIZE) { ERROR_WITH_ERRNO("Reparse data of \"%s\" has size " "%"PRIu64" bytes (exceeds %u bytes)", inode_any_full_path(inode), blob->size, REPARSE_DATA_MAX_SIZE); return WIMLIB_ERR_INVALID_REPARSE_DATA; } ctx->reparse_ptr = ctx->reparse_data; return 0; } wimlib_assert(stream_is_unnamed_data_stream(strm)); /* Unnamed data stream of "regular" file */ /* This should be ensured by extract_blob_list() */ wimlib_assert(ctx->num_open_fds < MAX_OPEN_FILES); first_dentry = inode_first_extraction_dentry(inode); first_path = unix_build_extraction_path(first_dentry, ctx); retry_create: fd = open(first_path, O_EXCL | O_CREAT | O_WRONLY | O_NOFOLLOW, 0644); if (fd < 0) { if (errno == EEXIST && !unlink(first_path)) goto retry_create; ERROR_WITH_ERRNO("Can't create regular file \"%s\"", first_path); return WIMLIB_ERR_OPEN; } if (inode->i_attributes & FILE_ATTRIBUTE_SPARSE_FILE) { ctx->is_sparse_file[ctx->num_open_fds] = true; ctx->any_sparse_files = true; } else { ctx->is_sparse_file[ctx->num_open_fds] = false; #ifdef HAVE_POSIX_FALLOCATE posix_fallocate(fd, 0, blob->size); #endif } filedes_init(&ctx->open_fds[ctx->num_open_fds++], fd); return unix_create_hardlinks(inode, first_dentry, first_path, ctx); } /* Called when starting to read a blob for extraction */ static int unix_begin_extract_blob(struct blob_descriptor *blob, void *_ctx) { struct unix_apply_ctx *ctx = _ctx; const struct blob_extraction_target *targets = blob_extraction_targets(blob); for (u32 i = 0; i < blob->out_refcnt; i++) { int ret = unix_begin_extract_blob_instance(blob, targets[i].inode, targets[i].stream, ctx); if (ret) { ctx->reparse_ptr = NULL; unix_cleanup_open_fds(ctx, 0); return ret; } } return 0; } /* Called when the next chunk of a blob has been read for extraction */ static int unix_extract_chunk(const struct blob_descriptor *blob, u64 offset, const void *chunk, size_t size, void *_ctx) { struct unix_apply_ctx *ctx = _ctx; const void * const end = chunk + size; const void *p; bool zeroes; size_t len; unsigned i; int ret; /* * For sparse files, only write nonzero regions. This lets the * filesystem use holes to represent zero regions. */ for (p = chunk; p != end; p += len, offset += len) { zeroes = maybe_detect_sparse_region(p, end - p, &len, ctx->any_sparse_files); for (i = 0; i < ctx->num_open_fds; i++) { if (!zeroes || !ctx->is_sparse_file[i]) { ret = full_pwrite(&ctx->open_fds[i], p, len, offset); if (ret) goto err; } } } if (ctx->reparse_ptr) ctx->reparse_ptr = mempcpy(ctx->reparse_ptr, chunk, size); return 0; err: ERROR_WITH_ERRNO("Error writing data to filesystem"); return ret; } /* Called when a blob has been fully read for extraction */ static int unix_end_extract_blob(struct blob_descriptor *blob, int status, void *_ctx) { struct unix_apply_ctx *ctx = _ctx; int ret; unsigned j; const struct blob_extraction_target *targets = blob_extraction_targets(blob); ctx->reparse_ptr = NULL; if (status) { unix_cleanup_open_fds(ctx, 0); return status; } j = 0; ret = 0; for (u32 i = 0; i < blob->out_refcnt; i++) { struct wim_inode *inode = targets[i].inode; if (inode_is_symlink(inode)) { /* We finally have the symlink data, so we can create * the symlink. */ const char *path; path = unix_build_inode_extraction_path(inode, ctx); ret = unix_create_symlink(inode, path, blob->size, ctx); if (ret) { ERROR_WITH_ERRNO("Can't create symbolic link " "\"%s\"", path); break; } ret = unix_set_metadata(-1, inode, path, ctx); if (ret) break; } else { struct filedes *fd = &ctx->open_fds[j]; /* If the file is sparse, extend it to its final size. */ if (ctx->is_sparse_file[j] && ftruncate(fd->fd, blob->size)) { ERROR_WITH_ERRNO("Error extending \"%s\" to final size", unix_build_inode_extraction_path(inode, ctx)); ret = WIMLIB_ERR_WRITE; break; } /* Set metadata on regular file just before closing. */ ret = unix_set_metadata(fd->fd, inode, NULL, ctx); if (ret) break; if (filedes_close(fd)) { ERROR_WITH_ERRNO("Error closing \"%s\"", unix_build_inode_extraction_path(inode, ctx)); ret = WIMLIB_ERR_WRITE; break; } j++; } } unix_cleanup_open_fds(ctx, j); return ret; } static int unix_set_dir_metadata(struct list_head *dentry_list, struct unix_apply_ctx *ctx) { const struct wim_dentry *dentry; int ret; list_for_each_entry_reverse(dentry, dentry_list, d_extraction_list_node) { if (should_extract_as_directory(dentry->d_inode)) { ret = unix_set_metadata(-1, dentry->d_inode, NULL, ctx); if (ret) return ret; ret = report_file_metadata_applied(&ctx->common); if (ret) return ret; } } return 0; } static int unix_extract(struct list_head *dentry_list, struct apply_ctx *_ctx) { int ret; struct unix_apply_ctx *ctx = (struct unix_apply_ctx *)_ctx; size_t path_max; u64 dir_count; u64 empty_file_count; /* Compute the maximum path length that will be needed, then allocate * some path buffers. */ path_max = unix_compute_path_max(dentry_list, ctx); for (unsigned i = 0; i < NUM_PATHBUFS; i++) { ctx->pathbufs[i] = MALLOC(path_max); if (!ctx->pathbufs[i]) { ret = WIMLIB_ERR_NOMEM; goto out; } /* Pre-fill the target in each path buffer. We'll just append * the rest of the paths after this. */ memcpy(ctx->pathbufs[i], ctx->common.target, ctx->common.target_nchars); } /* Extract directories and empty regular files. Directories are needed * because we can't extract any other files until their directories * exist. Empty files are needed because they don't have * representatives in the blob list. */ unix_count_dentries(dentry_list, &dir_count, &empty_file_count); ret = start_file_structure_phase(&ctx->common, dir_count + empty_file_count); if (ret) goto out; ret = unix_create_dirs_and_empty_files(dentry_list, ctx); if (ret) goto out; ret = end_file_structure_phase(&ctx->common); if (ret) goto out; /* Get full path to target if needed for absolute symlink fixups. */ if ((ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX) && ctx->common.required_features.symlink_reparse_points) { ctx->target_abspath = realpath(ctx->common.target, NULL); if (!ctx->target_abspath) { ret = WIMLIB_ERR_NOMEM; goto out; } ctx->target_abspath_nchars = strlen(ctx->target_abspath); } /* Extract nonempty regular files and symbolic links. */ struct read_blob_callbacks cbs = { .begin_blob = unix_begin_extract_blob, .continue_blob = unix_extract_chunk, .end_blob = unix_end_extract_blob, .ctx = ctx, }; ret = extract_blob_list(&ctx->common, &cbs); if (ret) goto out; /* Set directory metadata. We do this last so that we get the right * directory timestamps. */ ret = start_file_metadata_phase(&ctx->common, dir_count); if (ret) goto out; ret = unix_set_dir_metadata(dentry_list, ctx); if (ret) goto out; ret = end_file_metadata_phase(&ctx->common); if (ret) goto out; if (ctx->num_special_files_ignored) { WARNING("%lu special files were not extracted due to EPERM!", ctx->num_special_files_ignored); } out: for (unsigned i = 0; i < NUM_PATHBUFS; i++) FREE(ctx->pathbufs[i]); FREE(ctx->target_abspath); return ret; } const struct apply_operations unix_apply_ops = { .name = "UNIX", .get_supported_features = unix_get_supported_features, .extract = unix_extract, .context_size = sizeof(struct unix_apply_ctx), }; wimlib-1.13.1/src/sha1-ssse3.asm0000644000175000017500000003012313160354225013164 00000000000000;--------------------- ; ; This code implements two interfaces of SHA-1 update function: 1) working on a single ; 64-byte block and 2) working on a buffer of multiple 64-bit blocks. Multiple blocks ; version of code is software pipelined and faster overall, it is a default. Assemble ; with -DINTEL_SHA1_SINGLEBLOCK to select single 64-byte block function interface. ; ; C++ prototypes of implemented functions are below: ; ; #ifndef INTEL_SHA1_SINGLEBLOCK ; // Updates 20-byte SHA-1 record in 'hash' for 'num_blocks' consecutive 64-byte blocks ; extern "C" void sha1_update_intel(int *hash, const char* input, size_t num_blocks ); ; #else ; // Updates 20-byte SHA-1 record in 'hash' for one 64-byte block pointed by 'input' ; extern "C" void sha1_update_intel(int *hash, const char* input); ; #endif ; ; Function name 'sha1_update_intel' can be changed in the source or via macro: ; -DINTEL_SHA1_UPDATE_FUNCNAME=my_sha1_update_func_name ; ; It implements both UNIX(default) and Windows ABIs, use -DWIN_ABI on Windows ; ; Code checks CPU for SSSE3 support via CPUID feature flag (CPUID.1.ECX.SSSE3[bit 9]==1), ; and performs dispatch. Since in most cases the functionality on non-SSSE3 supporting CPUs ; is also required, the default (e.g. one being replaced) function can be provided for ; dispatch on such CPUs, the name of old function can be changed in the source or via macro: ; -DINTEL_SHA1_UPDATE_DEFAULT_DISPATCH=default_sha1_update_function_name ; ; Authors: Maxim Locktyukhin and Ronen Zohar at Intel.com ; %ifndef INTEL_SHA1_UPDATE_DEFAULT_DISPATCH ;; can be replaced with a default SHA-1 update function name %define INTEL_SHA1_UPDATE_DEFAULT_DISPATCH sha1_intel_non_ssse3_cpu_stub_ %else extern INTEL_SHA1_UPDATE_DEFAULT_DISPATCH %endif ;; provide alternative SHA-1 update function's name here %ifndef INTEL_SHA1_UPDATE_FUNCNAME %define INTEL_SHA1_UPDATE_FUNCNAME sha1_update_intel %endif global INTEL_SHA1_UPDATE_FUNCNAME %ifndef INTEL_SHA1_SINGLEBLOCK %assign multiblock 1 %else %assign multiblock 0 %endif bits 64 default rel %ifdef WIN_ABI %xdefine arg1 rcx %xdefine arg2 rdx %xdefine arg3 r8 %else %xdefine arg1 rdi %xdefine arg2 rsi %xdefine arg3 rdx %endif %xdefine ctx arg1 %xdefine buf arg2 %xdefine cnt arg3 %macro REGALLOC 0 %xdefine A ecx %xdefine B esi %xdefine C edi %xdefine D ebp %xdefine E edx %xdefine T1 eax %xdefine T2 ebx %endmacro %xdefine K_BASE r8 %xdefine HASH_PTR r9 %xdefine BUFFER_PTR r10 %xdefine BUFFER_END r11 %xdefine W_TMP xmm0 %xdefine W_TMP2 xmm9 %xdefine W0 xmm1 %xdefine W4 xmm2 %xdefine W8 xmm3 %xdefine W12 xmm4 %xdefine W16 xmm5 %xdefine W20 xmm6 %xdefine W24 xmm7 %xdefine W28 xmm8 %xdefine XMM_SHUFB_BSWAP xmm10 ;; we keep window of 64 w[i]+K pre-calculated values in a circular buffer %xdefine WK(t) (rsp + (t & 15)*4) ;------------------------------------------------------------------------------ ; ; macro implements SHA-1 function's body for single or several 64-byte blocks ; first param: function's name ; second param: =0 - function implements single 64-byte block hash ; =1 - function implements multiple64-byte blocks hash ; 3rd function's argument is a number, greater 0, of 64-byte blocks to calc hash for ; %macro SHA1_VECTOR_ASM 2 align 4096 %1: push rbx push rbp %ifdef WIN_ABI push rdi push rsi %xdefine stack_size (16*4 + 16*5 + 8) %else %xdefine stack_size (16*4 + 8) %endif sub rsp, stack_size %ifdef WIN_ABI %xdefine xmm_save_base (rsp + 16*4) xmm_mov [xmm_save_base + 0*16], xmm6 xmm_mov [xmm_save_base + 1*16], xmm7 xmm_mov [xmm_save_base + 2*16], xmm8 xmm_mov [xmm_save_base + 3*16], xmm9 xmm_mov [xmm_save_base + 4*16], xmm10 %endif mov HASH_PTR, ctx mov BUFFER_PTR, buf %if (%2 == 1) shl cnt, 6 ;; mul by 64 add cnt, buf mov BUFFER_END, cnt %endif lea K_BASE, [K_XMM_AR] xmm_mov XMM_SHUFB_BSWAP, [bswap_shufb_ctl] SHA1_PIPELINED_MAIN_BODY %2 %ifdef WIN_ABI xmm_mov xmm6, [xmm_save_base + 0*16] xmm_mov xmm7, [xmm_save_base + 1*16] xmm_mov xmm8, [xmm_save_base + 2*16] xmm_mov xmm9, [xmm_save_base + 3*16] xmm_mov xmm10,[xmm_save_base + 4*16] %endif add rsp, stack_size %ifdef WIN_ABI pop rsi pop rdi %endif pop rbp pop rbx ret %endmacro ;-------------------------------------------- ; macro implements 80 rounds of SHA-1, for one 64-byte block or multiple blocks with s/w pipelining ; macro param: =0 - process single 64-byte block ; =1 - multiple blocks ; %macro SHA1_PIPELINED_MAIN_BODY 1 REGALLOC mov A, [HASH_PTR ] mov B, [HASH_PTR+ 4] mov C, [HASH_PTR+ 8] mov D, [HASH_PTR+12] mov E, [HASH_PTR+16] %assign i 0 %rep W_PRECALC_AHEAD W_PRECALC i %assign i i+1 %endrep %xdefine F F1 %if (%1 == 1) ;; code loops through more than one block %%_loop: cmp BUFFER_PTR, K_BASE ;; we use K_BASE value as a signal of a last block, jne %%_begin ;; it is set below by: cmovae BUFFER_PTR, K_BASE jmp %%_end align 32 %%_begin: %endif RR A,B,C,D,E,0 RR D,E,A,B,C,2 RR B,C,D,E,A,4 RR E,A,B,C,D,6 RR C,D,E,A,B,8 RR A,B,C,D,E,10 RR D,E,A,B,C,12 RR B,C,D,E,A,14 RR E,A,B,C,D,16 RR C,D,E,A,B,18 %xdefine F F2 RR A,B,C,D,E,20 RR D,E,A,B,C,22 RR B,C,D,E,A,24 RR E,A,B,C,D,26 RR C,D,E,A,B,28 RR A,B,C,D,E,30 RR D,E,A,B,C,32 RR B,C,D,E,A,34 RR E,A,B,C,D,36 RR C,D,E,A,B,38 %xdefine F F3 RR A,B,C,D,E,40 RR D,E,A,B,C,42 RR B,C,D,E,A,44 RR E,A,B,C,D,46 RR C,D,E,A,B,48 RR A,B,C,D,E,50 RR D,E,A,B,C,52 RR B,C,D,E,A,54 RR E,A,B,C,D,56 RR C,D,E,A,B,58 %xdefine F F4 %if (%1 == 1) ;; if code loops through more than one block add BUFFER_PTR, 64 ;; move to next 64-byte block cmp BUFFER_PTR, BUFFER_END ;; check if current block is the last one cmovae BUFFER_PTR, K_BASE ;; smart way to signal the last iteration %else %xdefine W_NO_TAIL_PRECALC 1 ;; no software pipelining for single block interface %endif RR A,B,C,D,E,60 RR D,E,A,B,C,62 RR B,C,D,E,A,64 RR E,A,B,C,D,66 RR C,D,E,A,B,68 RR A,B,C,D,E,70 RR D,E,A,B,C,72 RR B,C,D,E,A,74 RR E,A,B,C,D,76 RR C,D,E,A,B,78 UPDATE_HASH [HASH_PTR ],A UPDATE_HASH [HASH_PTR+ 4],B UPDATE_HASH [HASH_PTR+ 8],C UPDATE_HASH [HASH_PTR+12],D UPDATE_HASH [HASH_PTR+16],E %if (%1 == 1) jmp %%_loop align 32 %%_end: %endif %xdefine W_NO_TAIL_PRECALC 0 %xdefine F %error %endmacro %macro F1 3 mov T1,%2 xor T1,%3 and T1,%1 xor T1,%3 %endmacro %macro F2 3 mov T1,%3 xor T1,%2 xor T1,%1 %endmacro %macro F3 3 mov T1,%2 mov T2,%1 or T1,%1 and T2,%2 and T1,%3 or T1,T2 %endmacro %define F4 F2 %macro UPDATE_HASH 2 add %2, %1 mov %1, %2 %endmacro %macro W_PRECALC 1 %xdefine i (%1) %if (i < 20) %xdefine K_XMM 0 %elif (i < 40) %xdefine K_XMM 16 %elif (i < 60) %xdefine K_XMM 32 %else %xdefine K_XMM 48 %endif %if (i<16 || (i>=80 && i<(80 + W_PRECALC_AHEAD))) %if (W_NO_TAIL_PRECALC == 0) %xdefine i ((%1) % 80) ;; pre-compute for the next iteration %if (i == 0) W_PRECALC_RESET %endif W_PRECALC_00_15 %endif %elif (i < 32) W_PRECALC_16_31 %elif (i < 80) ;; rounds 32-79 W_PRECALC_32_79 %endif %endmacro %macro W_PRECALC_RESET 0 %xdefine W W0 %xdefine W_minus_04 W4 %xdefine W_minus_08 W8 %xdefine W_minus_12 W12 %xdefine W_minus_16 W16 %xdefine W_minus_20 W20 %xdefine W_minus_24 W24 %xdefine W_minus_28 W28 %xdefine W_minus_32 W %endmacro %macro W_PRECALC_ROTATE 0 %xdefine W_minus_32 W_minus_28 %xdefine W_minus_28 W_minus_24 %xdefine W_minus_24 W_minus_20 %xdefine W_minus_20 W_minus_16 %xdefine W_minus_16 W_minus_12 %xdefine W_minus_12 W_minus_08 %xdefine W_minus_08 W_minus_04 %xdefine W_minus_04 W %xdefine W W_minus_32 %endmacro %xdefine W_PRECALC_AHEAD 16 %xdefine W_NO_TAIL_PRECALC 0 %xdefine xmm_mov movdqa %macro W_PRECALC_00_15 0 ;; message scheduling pre-compute for rounds 0-15 %if ((i & 3) == 0) ;; blended SSE and ALU instruction scheduling, 1 vector iteration per 4 rounds movdqu W_TMP, [BUFFER_PTR + (i * 4)] %elif ((i & 3) == 1) pshufb W_TMP, XMM_SHUFB_BSWAP movdqa W, W_TMP %elif ((i & 3) == 2) paddd W_TMP, [K_BASE] %elif ((i & 3) == 3) movdqa [WK(i&~3)], W_TMP W_PRECALC_ROTATE %endif %endmacro %macro W_PRECALC_16_31 0 ;; message scheduling pre-compute for rounds 16-31 ;; calculating last 32 w[i] values in 8 XMM registers ;; pre-calculate K+w[i] values and store to mem, for later load by ALU add instruction ;; ;; "brute force" vectorization for rounds 16-31 only due to w[i]->w[i-3] dependency ;; %if ((i & 3) == 0) ;; blended SSE and ALU instruction scheduling, 1 vector iteration per 4 rounds movdqa W, W_minus_12 palignr W, W_minus_16, 8 ;; w[i-14] movdqa W_TMP, W_minus_04 psrldq W_TMP, 4 ;; w[i-3] pxor W, W_minus_08 %elif ((i & 3) == 1) pxor W_TMP, W_minus_16 pxor W, W_TMP movdqa W_TMP2, W movdqa W_TMP, W pslldq W_TMP2, 12 %elif ((i & 3) == 2) psrld W, 31 pslld W_TMP, 1 por W_TMP, W movdqa W, W_TMP2 psrld W_TMP2, 30 pslld W, 2 %elif ((i & 3) == 3) pxor W_TMP, W pxor W_TMP, W_TMP2 movdqa W, W_TMP paddd W_TMP, [K_BASE + K_XMM] movdqa [WK(i&~3)],W_TMP W_PRECALC_ROTATE %endif %endmacro %macro W_PRECALC_32_79 0 ;; in SHA-1 specification: w[i] = (w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]) rol 1 ;; instead we do equal: w[i] = (w[i-6] ^ w[i-16] ^ w[i-28] ^ w[i-32]) rol 2 ;; allows more efficient vectorization since w[i]=>w[i-3] dependency is broken ;; %if ((i & 3) == 0) ;; blended SSE and ALU instruction scheduling, 1 vector iteration per 4 rounds movdqa W_TMP, W_minus_04 pxor W, W_minus_28 ;; W is W_minus_32 before xor palignr W_TMP, W_minus_08, 8 %elif ((i & 3) == 1) pxor W, W_minus_16 pxor W, W_TMP movdqa W_TMP, W %elif ((i & 3) == 2) psrld W, 30 pslld W_TMP, 2 por W_TMP, W %elif ((i & 3) == 3) movdqa W, W_TMP paddd W_TMP, [K_BASE + K_XMM] movdqa [WK(i&~3)],W_TMP W_PRECALC_ROTATE %endif %endmacro %macro RR 6 ;; RR does two rounds of SHA-1 back to back with W pre-calculation ;; TEMP = A ;; A = F( i, B, C, D ) + E + ROTATE_LEFT( A, 5 ) + W[i] + K(i) ;; C = ROTATE_LEFT( B, 30 ) ;; D = C ;; E = D ;; B = TEMP W_PRECALC (%6 + W_PRECALC_AHEAD) F %2, %3, %4 ;; F returns result in T1 add %5, [WK(%6)] rol %2, 30 mov T2, %1 add %4, [WK(%6 + 1)] rol T2, 5 add %5, T1 W_PRECALC (%6 + W_PRECALC_AHEAD + 1) add T2, %5 mov %5, T2 rol T2, 5 add %4, T2 F %1, %2, %3 ;; F returns result in T1 add %4, T1 rol %1, 30 ;; write: %1, %2 ;; rotate: %1<=%4, %2<=%5, %3<=%1, %4<=%2, %5<=%3 %endmacro ;;---------------------- section .data align 128 %xdefine K1 0x5a827999 %xdefine K2 0x6ed9eba1 %xdefine K3 0x8f1bbcdc %xdefine K4 0xca62c1d6 align 128 K_XMM_AR: DD K1, K1, K1, K1 DD K2, K2, K2, K2 DD K3, K3, K3, K3 DD K4, K4, K4, K4 align 16 bswap_shufb_ctl: DD 00010203h DD 04050607h DD 08090a0bh DD 0c0d0e0fh ;; dispatch pointer, points to the init routine for the first invocation sha1_update_intel_dispatched: DQ sha1_update_intel_init_ ;;---------------------- section .text align 4096 SHA1_VECTOR_ASM sha1_update_intel_ssse3_, multiblock align 32 sha1_update_intel_init_: ;; we get here with the first time invocation call sha1_update_intel_dispacth_init_ INTEL_SHA1_UPDATE_FUNCNAME: ;; we get here after init jmp qword [sha1_update_intel_dispatched] ;; CPUID feature flag based dispatch sha1_update_intel_dispacth_init_: push rax push rbx push rcx push rdx push rsi lea rsi, [INTEL_SHA1_UPDATE_DEFAULT_DISPATCH] mov eax, 1 cpuid test ecx, 0200h ;; SSSE3 support, CPUID.1.ECX[bit 9] jz _done lea rsi, [sha1_update_intel_ssse3_] _done: mov [sha1_update_intel_dispatched], rsi pop rsi pop rdx pop rcx pop rbx pop rax ret ;;---------------------- ;; in the case a default SHA-1 update function implementation was not provided ;; and code was invoked on a non-SSSE3 supporting CPU, dispatch handles this ;; failure in a safest way - jumps to the stub function with UD2 instruction below sha1_intel_non_ssse3_cpu_stub_: ud2 ;; in the case no default SHA-1 was provided non-SSSE3 CPUs safely fail here ret ; END ;---------------------- wimlib-1.13.1/src/extract.c0000644000175000017500000016420113324714101012406 00000000000000/* * extract.c * * Support for extracting WIM images, or files or directories contained in a WIM * image. */ /* * Copyright (C) 2012-2018 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ /* * This file provides the API functions wimlib_extract_image(), * wimlib_extract_image_from_pipe(), wimlib_extract_paths(), and * wimlib_extract_pathlist(). Internally, all end up calling * do_wimlib_extract_paths() and extract_trees(). * * Although wimlib supports multiple extraction modes/backends (NTFS-3G, UNIX, * Win32), this file does not itself have code to extract files or directories * to any specific target; instead, it handles generic functionality and relies * on lower-level callback functions declared in `struct apply_operations' to do * the actual extraction. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include "wimlib/apply.h" #include "wimlib/assert.h" #include "wimlib/blob_table.h" #include "wimlib/dentry.h" #include "wimlib/encoding.h" #include "wimlib/endianness.h" #include "wimlib/error.h" #include "wimlib/metadata.h" #include "wimlib/object_id.h" #include "wimlib/pathlist.h" #include "wimlib/paths.h" #include "wimlib/pattern.h" #include "wimlib/reparse.h" #include "wimlib/resource.h" #include "wimlib/security.h" #include "wimlib/unix_data.h" #include "wimlib/wim.h" #include "wimlib/win32.h" /* for realpath() equivalent */ #include "wimlib/xattr.h" #include "wimlib/xml.h" #define WIMLIB_EXTRACT_FLAG_FROM_PIPE 0x80000000 #define WIMLIB_EXTRACT_FLAG_IMAGEMODE 0x40000000 /* Keep in sync with wimlib.h */ #define WIMLIB_EXTRACT_MASK_PUBLIC \ (WIMLIB_EXTRACT_FLAG_NTFS | \ WIMLIB_EXTRACT_FLAG_UNIX_DATA | \ WIMLIB_EXTRACT_FLAG_NO_ACLS | \ WIMLIB_EXTRACT_FLAG_STRICT_ACLS | \ WIMLIB_EXTRACT_FLAG_RPFIX | \ WIMLIB_EXTRACT_FLAG_NORPFIX | \ WIMLIB_EXTRACT_FLAG_TO_STDOUT | \ WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES | \ WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS | \ WIMLIB_EXTRACT_FLAG_STRICT_TIMESTAMPS | \ WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES | \ WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS | \ WIMLIB_EXTRACT_FLAG_GLOB_PATHS | \ WIMLIB_EXTRACT_FLAG_STRICT_GLOB | \ WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES | \ WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE | \ WIMLIB_EXTRACT_FLAG_WIMBOOT | \ WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K | \ WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K | \ WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K | \ WIMLIB_EXTRACT_FLAG_COMPACT_LZX \ ) /* Send WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE or * WIMLIB_PROGRESS_MSG_EXTRACT_METADATA. */ int do_file_extract_progress(struct apply_ctx *ctx, enum wimlib_progress_msg msg) { ctx->count_until_file_progress = 500; /* Arbitrary value to limit calls */ return extract_progress(ctx, msg); } static int start_file_phase(struct apply_ctx *ctx, u64 end_file_count, enum wimlib_progress_msg msg) { ctx->progress.extract.current_file_count = 0; ctx->progress.extract.end_file_count = end_file_count; return do_file_extract_progress(ctx, msg); } int start_file_structure_phase(struct apply_ctx *ctx, u64 end_file_count) { return start_file_phase(ctx, end_file_count, WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE); } int start_file_metadata_phase(struct apply_ctx *ctx, u64 end_file_count) { return start_file_phase(ctx, end_file_count, WIMLIB_PROGRESS_MSG_EXTRACT_METADATA); } static int end_file_phase(struct apply_ctx *ctx, enum wimlib_progress_msg msg) { ctx->progress.extract.current_file_count = ctx->progress.extract.end_file_count; return do_file_extract_progress(ctx, msg); } int end_file_structure_phase(struct apply_ctx *ctx) { return end_file_phase(ctx, WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE); } int end_file_metadata_phase(struct apply_ctx *ctx) { return end_file_phase(ctx, WIMLIB_PROGRESS_MSG_EXTRACT_METADATA); } /* Are all bytes in the specified buffer zero? */ static bool is_all_zeroes(const u8 *p, const size_t size) { const u8 * const end = p + size; for (; (uintptr_t)p % WORDBYTES && p != end; p++) if (*p) return false; for (; end - p >= WORDBYTES; p += WORDBYTES) if (*(const machine_word_t *)p) return false; for (; p != end; p++) if (*p) return false; return true; } /* * Sparse regions should be detected at the granularity of the filesystem block * size. For now just assume 4096 bytes, which is the default block size on * NTFS and most Linux filesystems. */ #define SPARSE_UNIT 4096 /* * Detect whether the specified buffer begins with a region of all zero bytes. * Return %true if a zero region was found or %false if a nonzero region was * found, and sets *len_ret to the length of the region. This operates at a * granularity of SPARSE_UNIT bytes, meaning that to extend a zero region, there * must be SPARSE_UNIT zero bytes with no interruption, but to extend a nonzero * region, just one nonzero byte in the next SPARSE_UNIT bytes is sufficient. * * Note: besides compression, the WIM format doesn't yet have a way to * efficiently represent zero regions, so that's why we need to detect them * ourselves. Things will still fall apart badly on extremely large sparse * files, but this is a start... */ bool detect_sparse_region(const void *data, size_t size, size_t *len_ret) { const void *p = data; const void * const end = data + size; size_t len = 0; bool zeroes = false; while (p != end) { size_t n = min(end - p, SPARSE_UNIT); bool z = is_all_zeroes(p, n); if (len != 0 && z != zeroes) break; zeroes = z; len += n; p += n; } *len_ret = len; return zeroes; } #define PWM_FOUND_WIM_HDR (-1) /* Read the header for a blob in a pipable WIM. If @pwm_hdr_ret is not NULL, * also look for a pipable WIM header and return PWM_FOUND_WIM_HDR if found. */ static int read_pwm_blob_header(WIMStruct *pwm, u8 hash_ret[SHA1_HASH_SIZE], struct wim_reshdr *reshdr_ret, struct wim_header_disk *pwm_hdr_ret) { int ret; struct pwm_blob_hdr blob_hdr; u64 magic; ret = full_read(&pwm->in_fd, &blob_hdr, sizeof(blob_hdr)); if (unlikely(ret)) goto read_error; magic = le64_to_cpu(blob_hdr.magic); if (magic == PWM_MAGIC && pwm_hdr_ret != NULL) { memcpy(pwm_hdr_ret, &blob_hdr, sizeof(blob_hdr)); ret = full_read(&pwm->in_fd, (u8 *)pwm_hdr_ret + sizeof(blob_hdr), sizeof(*pwm_hdr_ret) - sizeof(blob_hdr)); if (unlikely(ret)) goto read_error; return PWM_FOUND_WIM_HDR; } if (unlikely(magic != PWM_BLOB_MAGIC)) { ERROR("Data read on pipe is invalid (expected blob header)"); return WIMLIB_ERR_INVALID_PIPABLE_WIM; } copy_hash(hash_ret, blob_hdr.hash); reshdr_ret->size_in_wim = 0; /* Not available */ reshdr_ret->flags = le32_to_cpu(blob_hdr.flags); reshdr_ret->offset_in_wim = pwm->in_fd.offset; reshdr_ret->uncompressed_size = le64_to_cpu(blob_hdr.uncompressed_size); if (unlikely(reshdr_ret->uncompressed_size == 0)) { ERROR("Data read on pipe is invalid (resource is of 0 size)"); return WIMLIB_ERR_INVALID_PIPABLE_WIM; } return 0; read_error: if (ret == WIMLIB_ERR_UNEXPECTED_END_OF_FILE) ERROR("The pipe ended before all needed data was sent!"); else ERROR_WITH_ERRNO("Error reading pipable WIM from pipe"); return ret; } static int read_blobs_from_pipe(struct apply_ctx *ctx, const struct read_blob_callbacks *cbs) { int ret; u8 hash[SHA1_HASH_SIZE]; struct wim_reshdr reshdr; struct wim_header_disk pwm_hdr; struct wim_resource_descriptor rdesc; struct blob_descriptor *blob; copy_guid(ctx->progress.extract.guid, ctx->wim->hdr.guid); ctx->progress.extract.part_number = ctx->wim->hdr.part_number; ctx->progress.extract.total_parts = ctx->wim->hdr.total_parts; ret = extract_progress(ctx, WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN); if (ret) return ret; while (ctx->num_blobs_remaining) { ret = read_pwm_blob_header(ctx->wim, hash, &reshdr, &pwm_hdr); if (ret == PWM_FOUND_WIM_HDR) { u16 part_number = le16_to_cpu(pwm_hdr.part_number); u16 total_parts = le16_to_cpu(pwm_hdr.total_parts); if (part_number == ctx->progress.extract.part_number && total_parts == ctx->progress.extract.total_parts && guids_equal(pwm_hdr.guid, ctx->progress.extract.guid)) continue; copy_guid(ctx->progress.extract.guid, pwm_hdr.guid); ctx->progress.extract.part_number = part_number; ctx->progress.extract.total_parts = total_parts; ret = extract_progress(ctx, WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN); if (ret) return ret; continue; } if (ret) return ret; if (!(reshdr.flags & WIM_RESHDR_FLAG_METADATA) && (blob = lookup_blob(ctx->wim->blob_table, hash)) && (blob->out_refcnt)) { wim_reshdr_to_desc_and_blob(&reshdr, ctx->wim, &rdesc, blob); ret = read_blob_with_sha1(blob, cbs); blob_unset_is_located_in_wim_resource(blob); if (ret) return ret; ctx->num_blobs_remaining--; } else { wim_reshdr_to_desc(&reshdr, ctx->wim, &rdesc); ret = skip_wim_resource(&rdesc); if (ret) return ret; } } return 0; } static int handle_pwm_metadata_resource(WIMStruct *pwm, int image, bool is_needed) { struct blob_descriptor *blob; struct wim_reshdr reshdr; struct wim_resource_descriptor *rdesc; int ret; ret = WIMLIB_ERR_NOMEM; blob = new_blob_descriptor(); if (!blob) goto out; ret = read_pwm_blob_header(pwm, blob->hash, &reshdr, NULL); if (ret) goto out; ret = WIMLIB_ERR_INVALID_PIPABLE_WIM; if (!(reshdr.flags & WIM_RESHDR_FLAG_METADATA)) { ERROR("Expected metadata resource, but found non-metadata " "resource"); goto out; } ret = WIMLIB_ERR_NOMEM; rdesc = MALLOC(sizeof(*rdesc)); if (!rdesc) goto out; wim_reshdr_to_desc_and_blob(&reshdr, pwm, rdesc, blob); pwm->refcnt++; ret = WIMLIB_ERR_NOMEM; pwm->image_metadata[image - 1] = new_unloaded_image_metadata(blob); if (!pwm->image_metadata[image - 1]) goto out; blob = NULL; /* If the metadata resource is for the image being extracted, then parse * it and save the metadata in memory. Otherwise, skip over it. */ if (is_needed) ret = select_wim_image(pwm, image); else ret = skip_wim_resource(rdesc); out: free_blob_descriptor(blob); return ret; } /* Creates a temporary file opened for writing. The open file descriptor is * returned in @fd_ret and its name is returned in @name_ret (dynamically * allocated). */ static int create_temporary_file(struct filedes *fd_ret, tchar **name_ret) { tchar *name; int raw_fd; #ifdef __WIN32__ retry: name = _wtempnam(NULL, L"wimlib"); if (!name) { ERROR_WITH_ERRNO("Failed to create temporary filename"); return WIMLIB_ERR_NOMEM; } raw_fd = _wopen(name, O_WRONLY | O_CREAT | O_EXCL | O_BINARY | _O_SHORT_LIVED, 0600); if (raw_fd < 0 && errno == EEXIST) { FREE(name); goto retry; } #else /* __WIN32__ */ const char *tmpdir = getenv("TMPDIR"); if (!tmpdir) tmpdir = P_tmpdir; name = MALLOC(strlen(tmpdir) + 1 + 6 + 6 + 1); if (!name) return WIMLIB_ERR_NOMEM; sprintf(name, "%s/wimlibXXXXXX", tmpdir); raw_fd = mkstemp(name); #endif /* !__WIN32__ */ if (raw_fd < 0) { ERROR_WITH_ERRNO("Failed to create temporary file " "\"%"TS"\"", name); FREE(name); return WIMLIB_ERR_OPEN; } filedes_init(fd_ret, raw_fd); *name_ret = name; return 0; } static int begin_extract_blob(struct blob_descriptor *blob, void *_ctx) { struct apply_ctx *ctx = _ctx; if (unlikely(blob->out_refcnt > MAX_OPEN_FILES)) return create_temporary_file(&ctx->tmpfile_fd, &ctx->tmpfile_name); return call_begin_blob(blob, ctx->saved_cbs); } static int extract_chunk(const struct blob_descriptor *blob, u64 offset, const void *chunk, size_t size, void *_ctx) { struct apply_ctx *ctx = _ctx; union wimlib_progress_info *progress = &ctx->progress; bool last = (offset + size == blob->size); int ret; if (likely(ctx->supported_features.hard_links)) { progress->extract.completed_bytes += (u64)size * blob->out_refcnt; if (last) progress->extract.completed_streams += blob->out_refcnt; } else { const struct blob_extraction_target *targets = blob_extraction_targets(blob); for (u32 i = 0; i < blob->out_refcnt; i++) { const struct wim_inode *inode = targets[i].inode; const struct wim_dentry *dentry; inode_for_each_extraction_alias(dentry, inode) { progress->extract.completed_bytes += size; if (last) progress->extract.completed_streams++; } } } if (progress->extract.completed_bytes >= ctx->next_progress) { ret = extract_progress(ctx, WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS); if (ret) return ret; set_next_progress(progress->extract.completed_bytes, progress->extract.total_bytes, &ctx->next_progress); } if (unlikely(filedes_valid(&ctx->tmpfile_fd))) { /* Just extracting to temporary file for now. */ ret = full_write(&ctx->tmpfile_fd, chunk, size); if (ret) { ERROR_WITH_ERRNO("Error writing data to " "temporary file \"%"TS"\"", ctx->tmpfile_name); } return ret; } return call_continue_blob(blob, offset, chunk, size, ctx->saved_cbs); } /* Copy the blob's data from the temporary file to each of its targets. * * This is executed only in the very uncommon case that a blob is being * extracted to more than MAX_OPEN_FILES targets! */ static int extract_from_tmpfile(const tchar *tmpfile_name, const struct blob_descriptor *orig_blob, const struct read_blob_callbacks *cbs) { struct blob_descriptor tmpfile_blob; const struct blob_extraction_target *targets = blob_extraction_targets(orig_blob); int ret; memcpy(&tmpfile_blob, orig_blob, sizeof(struct blob_descriptor)); tmpfile_blob.blob_location = BLOB_IN_FILE_ON_DISK; tmpfile_blob.file_on_disk = (tchar *)tmpfile_name; tmpfile_blob.out_refcnt = 1; for (u32 i = 0; i < orig_blob->out_refcnt; i++) { tmpfile_blob.inline_blob_extraction_targets[0] = targets[i]; ret = read_blob_with_cbs(&tmpfile_blob, cbs); if (ret) return ret; } return 0; } static int end_extract_blob(struct blob_descriptor *blob, int status, void *_ctx) { struct apply_ctx *ctx = _ctx; if (unlikely(filedes_valid(&ctx->tmpfile_fd))) { filedes_close(&ctx->tmpfile_fd); if (!status) status = extract_from_tmpfile(ctx->tmpfile_name, blob, ctx->saved_cbs); filedes_invalidate(&ctx->tmpfile_fd); tunlink(ctx->tmpfile_name); FREE(ctx->tmpfile_name); return status; } return call_end_blob(blob, status, ctx->saved_cbs); } /* * Read the list of blobs to extract and feed their data into the specified * callback functions. * * This handles checksumming each blob. * * This also handles sending WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS. * * This also works if the WIM is being read from a pipe. * * This also will split up blobs that will need to be extracted to more than * MAX_OPEN_FILES locations, as measured by the 'out_refcnt' of each blob. * Therefore, the apply_operations implementation need not worry about running * out of file descriptors, unless it might open more than one file descriptor * per 'blob_extraction_target' (e.g. Win32 currently might because the * destination file system might not support hard links). */ int extract_blob_list(struct apply_ctx *ctx, const struct read_blob_callbacks *cbs) { struct read_blob_callbacks wrapper_cbs = { .begin_blob = begin_extract_blob, .continue_blob = extract_chunk, .end_blob = end_extract_blob, .ctx = ctx, }; ctx->saved_cbs = cbs; if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_FROM_PIPE) { return read_blobs_from_pipe(ctx, &wrapper_cbs); } else { return read_blob_list(&ctx->blob_list, offsetof(struct blob_descriptor, extraction_list), &wrapper_cbs, VERIFY_BLOB_HASHES); } } /* Extract a WIM dentry to standard output. * * This obviously doesn't make sense in all cases. We return an error if the * dentry does not correspond to a regular file. Otherwise we extract the * unnamed data stream only. */ static int extract_dentry_to_stdout(struct wim_dentry *dentry, const struct blob_table *blob_table) { struct wim_inode *inode = dentry->d_inode; struct blob_descriptor *blob; struct filedes _stdout; if (inode->i_attributes & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ENCRYPTED)) { ERROR("\"%"TS"\" is not a regular file and therefore cannot be " "extracted to standard output", dentry_full_path(dentry)); return WIMLIB_ERR_NOT_A_REGULAR_FILE; } blob = inode_get_blob_for_unnamed_data_stream(inode, blob_table); if (!blob) { const u8 *hash = inode_get_hash_of_unnamed_data_stream(inode); if (!is_zero_hash(hash)) return blob_not_found_error(inode, hash); return 0; } filedes_init(&_stdout, STDOUT_FILENO); return extract_blob_to_fd(blob, &_stdout); } static int extract_dentries_to_stdout(struct wim_dentry **dentries, size_t num_dentries, const struct blob_table *blob_table) { for (size_t i = 0; i < num_dentries; i++) { int ret = extract_dentry_to_stdout(dentries[i], blob_table); if (ret) return ret; } return 0; } /**********************************************************************/ /* * Removes duplicate dentries from the array. * * Returns the new number of dentries, packed at the front of the array. */ static size_t remove_duplicate_trees(struct wim_dentry **trees, size_t num_trees) { size_t i, j = 0; for (i = 0; i < num_trees; i++) { if (!trees[i]->d_tmp_flag) { /* Found distinct dentry. */ trees[i]->d_tmp_flag = 1; trees[j++] = trees[i]; } } for (i = 0; i < j; i++) trees[i]->d_tmp_flag = 0; return j; } /* * Remove dentries that are descendants of other dentries in the array. * * Returns the new number of dentries, packed at the front of the array. */ static size_t remove_contained_trees(struct wim_dentry **trees, size_t num_trees) { size_t i, j = 0; for (i = 0; i < num_trees; i++) trees[i]->d_tmp_flag = 1; for (i = 0; i < num_trees; i++) { struct wim_dentry *d = trees[i]; while (!dentry_is_root(d)) { d = d->d_parent; if (d->d_tmp_flag) goto tree_contained; } trees[j++] = trees[i]; continue; tree_contained: trees[i]->d_tmp_flag = 0; } for (i = 0; i < j; i++) trees[i]->d_tmp_flag = 0; return j; } static int dentry_append_to_list(struct wim_dentry *dentry, void *_dentry_list) { struct list_head *dentry_list = _dentry_list; list_add_tail(&dentry->d_extraction_list_node, dentry_list); return 0; } static void dentry_reset_extraction_list_node(struct wim_dentry *dentry) { dentry->d_extraction_list_node = (struct list_head){NULL, NULL}; } static int dentry_delete_from_list(struct wim_dentry *dentry, void *_ignore) { if (will_extract_dentry(dentry)) { list_del(&dentry->d_extraction_list_node); dentry_reset_extraction_list_node(dentry); } return 0; } /* * Build the preliminary list of dentries to be extracted. * * The list maintains the invariant that if d1 and d2 are in the list and d1 is * an ancestor of d2, then d1 appears before d2 in the list. */ static void build_dentry_list(struct list_head *dentry_list, struct wim_dentry **trees, size_t num_trees, bool add_ancestors) { INIT_LIST_HEAD(dentry_list); /* Add the trees recursively. */ for (size_t i = 0; i < num_trees; i++) for_dentry_in_tree(trees[i], dentry_append_to_list, dentry_list); /* If requested, add ancestors of the trees. */ if (add_ancestors) { for (size_t i = 0; i < num_trees; i++) { struct wim_dentry *dentry = trees[i]; struct wim_dentry *ancestor; struct list_head *place_after; if (dentry_is_root(dentry)) continue; place_after = dentry_list; ancestor = dentry; do { ancestor = ancestor->d_parent; if (will_extract_dentry(ancestor)) { place_after = &ancestor->d_extraction_list_node; break; } } while (!dentry_is_root(ancestor)); ancestor = dentry; do { ancestor = ancestor->d_parent; if (will_extract_dentry(ancestor)) break; list_add(&ancestor->d_extraction_list_node, place_after); } while (!dentry_is_root(ancestor)); } } } static void destroy_dentry_list(struct list_head *dentry_list) { struct wim_dentry *dentry, *tmp; struct wim_inode *inode; list_for_each_entry_safe(dentry, tmp, dentry_list, d_extraction_list_node) { inode = dentry->d_inode; dentry_reset_extraction_list_node(dentry); inode->i_visited = 0; inode->i_can_externally_back = 0; if ((void *)dentry->d_extraction_name != (void *)dentry->d_name) FREE(dentry->d_extraction_name); dentry->d_extraction_name = NULL; dentry->d_extraction_name_nchars = 0; } } static void destroy_blob_list(struct list_head *blob_list) { struct blob_descriptor *blob; list_for_each_entry(blob, blob_list, extraction_list) if (blob->out_refcnt > ARRAY_LEN(blob->inline_blob_extraction_targets)) FREE(blob->blob_extraction_targets); } #ifdef __WIN32__ static const utf16lechar replacement_char = cpu_to_le16(0xfffd); #else static const utf16lechar replacement_char = cpu_to_le16('?'); #endif static bool file_name_valid(utf16lechar *name, size_t num_chars, bool fix) { size_t i; if (num_chars == 0) return true; for (i = 0; i < num_chars; i++) { switch (le16_to_cpu(name[i])) { #ifdef __WIN32__ case '\x01'...'\x1F': case '\\': case ':': case '*': case '?': case '"': case '<': case '>': case '|': #endif case '/': case '\0': if (fix) name[i] = replacement_char; else return false; } } return true; } static int dentry_calculate_extraction_name(struct wim_dentry *dentry, struct apply_ctx *ctx) { int ret; if (dentry_is_root(dentry)) return 0; #ifdef WITH_NTFS_3G if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) { dentry->d_extraction_name = dentry->d_name; dentry->d_extraction_name_nchars = dentry->d_name_nbytes / sizeof(utf16lechar); return 0; } #endif if (!ctx->supported_features.case_sensitive_filenames) { struct wim_dentry *other; dentry_for_each_ci_match(other, dentry) { if (will_extract_dentry(other)) { if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS) { WARNING("\"%"TS"\" has the same " "case-insensitive name as " "\"%"TS"\"; extracting " "dummy name instead", dentry_full_path(dentry), dentry_full_path(other)); goto out_replace; } else { WARNING("Not extracting \"%"TS"\": " "has same case-insensitive " "name as \"%"TS"\"", dentry_full_path(dentry), dentry_full_path(other)); goto skip_dentry; } } } } if (file_name_valid(dentry->d_name, dentry->d_name_nbytes / 2, false)) { size_t nbytes = 0; ret = utf16le_get_tstr(dentry->d_name, dentry->d_name_nbytes, (const tchar **)&dentry->d_extraction_name, &nbytes); dentry->d_extraction_name_nchars = nbytes / sizeof(tchar); return ret; } else { if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES) { WARNING("\"%"TS"\" has an invalid filename " "that is not supported on this platform; " "extracting dummy name instead", dentry_full_path(dentry)); goto out_replace; } else { WARNING("Not extracting \"%"TS"\": has an invalid filename " "that is not supported on this platform", dentry_full_path(dentry)); goto skip_dentry; } } out_replace: { utf16lechar utf16_name_copy[dentry->d_name_nbytes / 2]; memcpy(utf16_name_copy, dentry->d_name, dentry->d_name_nbytes); file_name_valid(utf16_name_copy, dentry->d_name_nbytes / 2, true); const tchar *tchar_name; size_t tchar_nchars; ret = utf16le_get_tstr(utf16_name_copy, dentry->d_name_nbytes, &tchar_name, &tchar_nchars); if (ret) return ret; tchar_nchars /= sizeof(tchar); size_t fixed_name_num_chars = tchar_nchars; tchar fixed_name[tchar_nchars + 50]; tmemcpy(fixed_name, tchar_name, tchar_nchars); fixed_name_num_chars += tsprintf(fixed_name + tchar_nchars, T(" (invalid filename #%lu)"), ++ctx->invalid_sequence); utf16le_put_tstr(tchar_name); dentry->d_extraction_name = TSTRDUP(fixed_name); if (!dentry->d_extraction_name) return WIMLIB_ERR_NOMEM; dentry->d_extraction_name_nchars = fixed_name_num_chars; } return 0; skip_dentry: for_dentry_in_tree(dentry, dentry_delete_from_list, NULL); return 0; } /* * Calculate the actual filename component at which each WIM dentry will be * extracted, with special handling for dentries that are unsupported by the * extraction backend or have invalid names. * * ctx->supported_features must be filled in. * * Possible error codes: WIMLIB_ERR_NOMEM, WIMLIB_ERR_INVALID_UTF16_STRING */ static int dentry_list_calculate_extraction_names(struct list_head *dentry_list, struct apply_ctx *ctx) { struct list_head *prev, *cur; /* Can't use list_for_each_entry() because a call to * dentry_calculate_extraction_name() may delete the current dentry and * its children from the list. */ prev = dentry_list; for (;;) { struct wim_dentry *dentry; int ret; cur = prev->next; if (cur == dentry_list) break; dentry = list_entry(cur, struct wim_dentry, d_extraction_list_node); ret = dentry_calculate_extraction_name(dentry, ctx); if (ret) return ret; if (prev->next == cur) prev = cur; else ; /* Current dentry and its children (which follow in the list) were deleted. prev stays the same. */ } return 0; } static int dentry_resolve_streams(struct wim_dentry *dentry, int extract_flags, struct blob_table *blob_table) { struct wim_inode *inode = dentry->d_inode; struct blob_descriptor *blob; int ret; bool force = false; /* Special case: when extracting from a pipe, the WIM blob table is * initially empty, so "resolving" an inode's streams is initially not * possible. However, we still need to keep track of which blobs, * identified by SHA-1 message digests, need to be extracted, so we * "resolve" the inode's streams anyway by allocating a 'struct * blob_descriptor' for each one. */ if (extract_flags & WIMLIB_EXTRACT_FLAG_FROM_PIPE) force = true; ret = inode_resolve_streams(inode, blob_table, force); if (ret) return ret; for (unsigned i = 0; i < inode->i_num_streams; i++) { blob = stream_blob_resolved(&inode->i_streams[i]); if (blob) blob->out_refcnt = 0; } return 0; } /* * For each dentry to be extracted, resolve all streams in the corresponding * inode and set 'out_refcnt' in all referenced blob_descriptors to 0. * * Possible error codes: WIMLIB_ERR_RESOURCE_NOT_FOUND, WIMLIB_ERR_NOMEM. */ static int dentry_list_resolve_streams(struct list_head *dentry_list, struct apply_ctx *ctx) { struct wim_dentry *dentry; int ret; list_for_each_entry(dentry, dentry_list, d_extraction_list_node) { ret = dentry_resolve_streams(dentry, ctx->extract_flags, ctx->wim->blob_table); if (ret) return ret; } return 0; } static int ref_stream(struct wim_inode_stream *strm, struct wim_dentry *dentry, struct apply_ctx *ctx) { struct wim_inode *inode = dentry->d_inode; struct blob_descriptor *blob = stream_blob_resolved(strm); struct blob_extraction_target *targets; if (!blob) return 0; /* Tally the size only for each actual extraction of the stream (not * additional hard links to the inode). */ if (inode->i_visited && ctx->supported_features.hard_links) return 0; ctx->progress.extract.total_bytes += blob->size; ctx->progress.extract.total_streams++; if (inode->i_visited) return 0; /* Add each blob to 'ctx->blob_list' only one time, regardless of how * many extraction targets it will have. */ if (blob->out_refcnt == 0) { list_add_tail(&blob->extraction_list, &ctx->blob_list); ctx->num_blobs_remaining++; } /* Set this stream as an extraction target of 'blob'. */ if (blob->out_refcnt < ARRAY_LEN(blob->inline_blob_extraction_targets)) { targets = blob->inline_blob_extraction_targets; } else { struct blob_extraction_target *prev_targets; size_t alloc_blob_extraction_targets; if (blob->out_refcnt == ARRAY_LEN(blob->inline_blob_extraction_targets)) { prev_targets = NULL; alloc_blob_extraction_targets = ARRAY_LEN(blob->inline_blob_extraction_targets); } else { prev_targets = blob->blob_extraction_targets; alloc_blob_extraction_targets = blob->alloc_blob_extraction_targets; } if (blob->out_refcnt == alloc_blob_extraction_targets) { alloc_blob_extraction_targets *= 2; targets = REALLOC(prev_targets, alloc_blob_extraction_targets * sizeof(targets[0])); if (!targets) return WIMLIB_ERR_NOMEM; if (!prev_targets) { memcpy(targets, blob->inline_blob_extraction_targets, sizeof(blob->inline_blob_extraction_targets)); } blob->blob_extraction_targets = targets; blob->alloc_blob_extraction_targets = alloc_blob_extraction_targets; } targets = blob->blob_extraction_targets; } targets[blob->out_refcnt].inode = inode; targets[blob->out_refcnt].stream = strm; blob->out_refcnt++; return 0; } static int ref_stream_if_needed(struct wim_dentry *dentry, struct wim_inode *inode, struct wim_inode_stream *strm, struct apply_ctx *ctx) { bool need_stream = false; switch (strm->stream_type) { case STREAM_TYPE_DATA: if (stream_is_named(strm)) { /* Named data stream */ if (ctx->supported_features.named_data_streams) need_stream = true; } else if (!(inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ENCRYPTED)) && !(inode_is_symlink(inode) && !ctx->supported_features.reparse_points && ctx->supported_features.symlink_reparse_points)) { /* * Unnamed data stream. Skip if any of the following is true: * * - file is a directory * - file is encrypted * - backend needs to create the file as UNIX symlink * - backend will extract the stream as externally * backed from the WIM archive itself */ if (ctx->apply_ops->will_back_from_wim) { int ret = (*ctx->apply_ops->will_back_from_wim)(dentry, ctx); if (ret > 0) /* Error? */ return ret; if (ret < 0) /* Won't externally back? */ need_stream = true; } else { need_stream = true; } } break; case STREAM_TYPE_REPARSE_POINT: wimlib_assert(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT); if (ctx->supported_features.reparse_points || (inode_is_symlink(inode) && ctx->supported_features.symlink_reparse_points)) need_stream = true; break; case STREAM_TYPE_EFSRPC_RAW_DATA: wimlib_assert(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED); if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) { if (ctx->supported_features.encrypted_directories) need_stream = true; } else { if (ctx->supported_features.encrypted_files) need_stream = true; } break; } if (need_stream) return ref_stream(strm, dentry, ctx); return 0; } static int dentry_ref_streams(struct wim_dentry *dentry, struct apply_ctx *ctx) { struct wim_inode *inode = dentry->d_inode; for (unsigned i = 0; i < inode->i_num_streams; i++) { int ret = ref_stream_if_needed(dentry, inode, &inode->i_streams[i], ctx); if (ret) return ret; } inode->i_visited = 1; return 0; } /* * Given a list of dentries to be extracted, build the list of blobs that need * to be extracted, and for each blob determine the streams to which that blob * will be extracted. * * This also initializes the extract progress info with byte and blob * information. * * ctx->supported_features must be filled in. */ static int dentry_list_ref_streams(struct list_head *dentry_list, struct apply_ctx *ctx) { struct wim_dentry *dentry; int ret; list_for_each_entry(dentry, dentry_list, d_extraction_list_node) { ret = dentry_ref_streams(dentry, ctx); if (ret) return ret; } list_for_each_entry(dentry, dentry_list, d_extraction_list_node) dentry->d_inode->i_visited = 0; return 0; } static void dentry_list_build_inode_alias_lists(struct list_head *dentry_list) { struct wim_dentry *dentry; list_for_each_entry(dentry, dentry_list, d_extraction_list_node) dentry->d_inode->i_first_extraction_alias = NULL; list_for_each_entry(dentry, dentry_list, d_extraction_list_node) { dentry->d_next_extraction_alias = dentry->d_inode->i_first_extraction_alias; dentry->d_inode->i_first_extraction_alias = dentry; } } static void inode_tally_features(const struct wim_inode *inode, struct wim_features *features) { if (inode->i_attributes & FILE_ATTRIBUTE_READONLY) features->readonly_files++; if (inode->i_attributes & FILE_ATTRIBUTE_HIDDEN) features->hidden_files++; if (inode->i_attributes & FILE_ATTRIBUTE_SYSTEM) features->system_files++; if (inode->i_attributes & FILE_ATTRIBUTE_ARCHIVE) features->archive_files++; if (inode->i_attributes & FILE_ATTRIBUTE_COMPRESSED) features->compressed_files++; if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) { if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) features->encrypted_directories++; else features->encrypted_files++; } if (inode->i_attributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) features->not_context_indexed_files++; if (inode->i_attributes & FILE_ATTRIBUTE_SPARSE_FILE) features->sparse_files++; if (inode_has_named_data_stream(inode)) features->named_data_streams++; if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) { features->reparse_points++; if (inode_is_symlink(inode)) features->symlink_reparse_points++; else features->other_reparse_points++; } if (inode_has_security_descriptor(inode)) features->security_descriptors++; if (inode_has_unix_data(inode)) features->unix_data++; if (inode_has_object_id(inode)) features->object_ids++; if (inode_has_xattrs(inode)) features->xattrs++; } /* Tally features necessary to extract a dentry and the corresponding inode. */ static void dentry_tally_features(struct wim_dentry *dentry, struct wim_features *features) { struct wim_inode *inode = dentry->d_inode; if (dentry_has_short_name(dentry)) features->short_names++; if (inode->i_visited) { features->hard_links++; } else { inode_tally_features(inode, features); inode->i_visited = 1; } } /* Tally the features necessary to extract the specified dentries. */ static void dentry_list_get_features(struct list_head *dentry_list, struct wim_features *features) { struct wim_dentry *dentry; list_for_each_entry(dentry, dentry_list, d_extraction_list_node) dentry_tally_features(dentry, features); list_for_each_entry(dentry, dentry_list, d_extraction_list_node) dentry->d_inode->i_visited = 0; } static int do_feature_check(const struct wim_features *required_features, const struct wim_features *supported_features, int extract_flags) { /* Encrypted files. */ if (required_features->encrypted_files && !supported_features->encrypted_files) WARNING("Ignoring EFS-encrypted data of %lu files", required_features->encrypted_files); /* Named data streams. */ if (required_features->named_data_streams && !supported_features->named_data_streams) WARNING("Ignoring named data streams of %lu files", required_features->named_data_streams); /* File attributes. */ if (!(extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES)) { if (required_features->readonly_files && !supported_features->readonly_files) WARNING("Ignoring FILE_ATTRIBUTE_READONLY of %lu files", required_features->readonly_files); if (required_features->hidden_files && !supported_features->hidden_files) WARNING("Ignoring FILE_ATTRIBUTE_HIDDEN of %lu files", required_features->hidden_files); if (required_features->system_files && !supported_features->system_files) WARNING("Ignoring FILE_ATTRIBUTE_SYSTEM of %lu files", required_features->system_files); /* Note: Don't bother the user about FILE_ATTRIBUTE_ARCHIVE. * We're an archive program, so theoretically we can do what we * want with it. */ if (required_features->compressed_files && !supported_features->compressed_files) WARNING("Ignoring FILE_ATTRIBUTE_COMPRESSED of %lu files", required_features->compressed_files); if (required_features->not_context_indexed_files && !supported_features->not_context_indexed_files) WARNING("Ignoring FILE_ATTRIBUTE_NOT_CONTENT_INDEXED of %lu files", required_features->not_context_indexed_files); if (required_features->sparse_files && !supported_features->sparse_files) WARNING("Ignoring FILE_ATTRIBUTE_SPARSE_FILE of %lu files", required_features->sparse_files); if (required_features->encrypted_directories && !supported_features->encrypted_directories) WARNING("Ignoring FILE_ATTRIBUTE_ENCRYPTED of %lu directories", required_features->encrypted_directories); } /* Hard links. */ if (required_features->hard_links && !supported_features->hard_links) WARNING("Extracting %lu hard links as independent files", required_features->hard_links); /* Symbolic links and reparse points. */ if ((extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS) && required_features->symlink_reparse_points && !supported_features->symlink_reparse_points && !supported_features->reparse_points) { ERROR("Extraction backend does not support symbolic links!"); return WIMLIB_ERR_UNSUPPORTED; } if (required_features->reparse_points && !supported_features->reparse_points) { if (supported_features->symlink_reparse_points) { if (required_features->other_reparse_points) { WARNING("Ignoring reparse data of %lu non-symlink/junction files", required_features->other_reparse_points); } } else { WARNING("Ignoring reparse data of %lu files", required_features->reparse_points); } } /* Security descriptors. */ if (((extract_flags & (WIMLIB_EXTRACT_FLAG_STRICT_ACLS | WIMLIB_EXTRACT_FLAG_UNIX_DATA)) == WIMLIB_EXTRACT_FLAG_STRICT_ACLS) && required_features->security_descriptors && !supported_features->security_descriptors) { ERROR("Extraction backend does not support security descriptors!"); return WIMLIB_ERR_UNSUPPORTED; } if (!(extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS) && required_features->security_descriptors && !supported_features->security_descriptors) WARNING("Ignoring Windows NT security descriptors of %lu files", required_features->security_descriptors); /* Standard UNIX metadata */ if (required_features->unix_data && (!supported_features->unix_data || !(extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA))) { if (extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) { ERROR("Requested UNIX metadata extraction, but " "extraction backend does not support it!"); return WIMLIB_ERR_UNSUPPORTED; } WARNING("Ignoring UNIX metadata (uid/gid/mode/rdev) of %lu files%"TS, required_features->unix_data, (supported_features->unix_data ? T("\n (use --unix-data mode to extract these)") : T(""))); } /* Extended attributes */ if (required_features->xattrs && (!supported_features->xattrs || (supported_features->unix_data && !(extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA)))) { WARNING("Ignoring extended attributes of %lu files%"TS, required_features->xattrs, (supported_features->xattrs ? T("\n (use --unix-data mode to extract these)") : T(""))); } /* Object IDs. */ if (required_features->object_ids && !supported_features->object_ids) { WARNING("Ignoring object IDs of %lu files", required_features->object_ids); } /* DOS Names. */ if (required_features->short_names && !supported_features->short_names) { if (extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES) { ERROR("Extraction backend does not support DOS names!"); return WIMLIB_ERR_UNSUPPORTED; } WARNING("Ignoring DOS names of %lu files", required_features->short_names); } /* Timestamps. */ if ((extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_TIMESTAMPS) && !supported_features->timestamps) { ERROR("Extraction backend does not support timestamps!"); return WIMLIB_ERR_UNSUPPORTED; } return 0; } static const struct apply_operations * select_apply_operations(int extract_flags) { #ifdef WITH_NTFS_3G if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) return &ntfs_3g_apply_ops; #endif #ifdef __WIN32__ return &win32_apply_ops; #else return &unix_apply_ops; #endif } static int extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees, const tchar *target, int extract_flags) { const struct apply_operations *ops; struct apply_ctx *ctx; int ret; LIST_HEAD(dentry_list); if (extract_flags & WIMLIB_EXTRACT_FLAG_TO_STDOUT) { ret = extract_dentries_to_stdout(trees, num_trees, wim->blob_table); goto out; } num_trees = remove_duplicate_trees(trees, num_trees); num_trees = remove_contained_trees(trees, num_trees); ops = select_apply_operations(extract_flags); if (num_trees > 1 && ops->single_tree_only) { ERROR("Extracting multiple directory trees " "at once is not supported in %s extraction mode!", ops->name); ret = WIMLIB_ERR_UNSUPPORTED; goto out; } ctx = CALLOC(1, ops->context_size); if (!ctx) { ret = WIMLIB_ERR_NOMEM; goto out; } ctx->wim = wim; ctx->target = target; ctx->target_nchars = tstrlen(target); ctx->extract_flags = extract_flags; if (ctx->wim->progfunc) { ctx->progfunc = ctx->wim->progfunc; ctx->progctx = ctx->wim->progctx; ctx->progress.extract.image = wim->current_image; ctx->progress.extract.extract_flags = (extract_flags & WIMLIB_EXTRACT_MASK_PUBLIC); ctx->progress.extract.wimfile_name = wim->filename; ctx->progress.extract.image_name = wimlib_get_image_name(wim, wim->current_image); ctx->progress.extract.target = target; } INIT_LIST_HEAD(&ctx->blob_list); filedes_invalidate(&ctx->tmpfile_fd); ctx->apply_ops = ops; ret = (*ops->get_supported_features)(target, &ctx->supported_features); if (ret) goto out_cleanup; build_dentry_list(&dentry_list, trees, num_trees, !(extract_flags & WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE)); dentry_list_get_features(&dentry_list, &ctx->required_features); ret = do_feature_check(&ctx->required_features, &ctx->supported_features, ctx->extract_flags); if (ret) goto out_cleanup; ret = dentry_list_calculate_extraction_names(&dentry_list, ctx); if (ret) goto out_cleanup; if (unlikely(list_empty(&dentry_list))) { WARNING("There is nothing to extract!"); goto out_cleanup; } ret = dentry_list_resolve_streams(&dentry_list, ctx); if (ret) goto out_cleanup; dentry_list_build_inode_alias_lists(&dentry_list); ret = dentry_list_ref_streams(&dentry_list, ctx); if (ret) goto out_cleanup; if (extract_flags & WIMLIB_EXTRACT_FLAG_FROM_PIPE) { /* When extracting from a pipe, the number of bytes of data to * extract can't be determined in the normal way (examining the * blob table), since at this point all we have is a set of * SHA-1 message digests of blobs that need to be extracted. * However, we can get a reasonably accurate estimate by taking * from the corresponding in the WIM XML * data. This does assume that a full image is being extracted, * but currently there is no API for doing otherwise. (Also, * subtract from this if hard links are * supported by the extraction mode.) */ ctx->progress.extract.total_bytes = xml_get_image_total_bytes(wim->xml_info, wim->current_image); if (ctx->supported_features.hard_links) { ctx->progress.extract.total_bytes -= xml_get_image_hard_link_bytes(wim->xml_info, wim->current_image); } } ret = extract_progress(ctx, ((extract_flags & WIMLIB_EXTRACT_FLAG_IMAGEMODE) ? WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN : WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN)); if (ret) goto out_cleanup; ret = (*ops->extract)(&dentry_list, ctx); if (ret) goto out_cleanup; if (ctx->progress.extract.completed_bytes < ctx->progress.extract.total_bytes) { ctx->progress.extract.completed_bytes = ctx->progress.extract.total_bytes; ret = extract_progress(ctx, WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS); if (ret) goto out_cleanup; } ret = extract_progress(ctx, ((extract_flags & WIMLIB_EXTRACT_FLAG_IMAGEMODE) ? WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END : WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END)); out_cleanup: destroy_blob_list(&ctx->blob_list); destroy_dentry_list(&dentry_list); FREE(ctx); out: return ret; } static int mkdir_if_needed(const tchar *target) { if (!tmkdir(target, 0755)) return 0; if (errno == EEXIST) return 0; #ifdef __WIN32__ /* _wmkdir() fails with EACCES if called on a drive root directory. */ if (errno == EACCES) return 0; #endif ERROR_WITH_ERRNO("Failed to create directory \"%"TS"\"", target); return WIMLIB_ERR_MKDIR; } /* Make sure the extraction flags make sense, and update them if needed. */ static int check_extract_flags(const WIMStruct *wim, int *extract_flags_p) { int extract_flags = *extract_flags_p; /* Check for invalid flag combinations */ if ((extract_flags & (WIMLIB_EXTRACT_FLAG_NO_ACLS | WIMLIB_EXTRACT_FLAG_STRICT_ACLS)) == (WIMLIB_EXTRACT_FLAG_NO_ACLS | WIMLIB_EXTRACT_FLAG_STRICT_ACLS)) return WIMLIB_ERR_INVALID_PARAM; if ((extract_flags & (WIMLIB_EXTRACT_FLAG_RPFIX | WIMLIB_EXTRACT_FLAG_NORPFIX)) == (WIMLIB_EXTRACT_FLAG_RPFIX | WIMLIB_EXTRACT_FLAG_NORPFIX)) return WIMLIB_ERR_INVALID_PARAM; #ifndef WITH_NTFS_3G if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) { ERROR("wimlib was compiled without support for NTFS-3G, so\n" " it cannot apply a WIM image directly to an NTFS volume."); return WIMLIB_ERR_UNSUPPORTED; } #endif if (extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT) { #ifdef __WIN32__ if (!wim->filename) return WIMLIB_ERR_NO_FILENAME; #else ERROR("WIMBoot extraction is only supported on Windows!"); return WIMLIB_ERR_UNSUPPORTED; #endif } if (extract_flags & (WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K | WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K | WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K | WIMLIB_EXTRACT_FLAG_COMPACT_LZX)) { #ifdef __WIN32__ int count = 0; count += ((extract_flags & WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K) != 0); count += ((extract_flags & WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K) != 0); count += ((extract_flags & WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K) != 0); count += ((extract_flags & WIMLIB_EXTRACT_FLAG_COMPACT_LZX) != 0); if (count != 1) { ERROR("Only one compression format can be specified " "for compact-mode extraction!"); return WIMLIB_ERR_INVALID_PARAM; } if (extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT) { ERROR("Compact-mode extraction and WIMBoot-mode " "extraction are mutually exclusive!"); return WIMLIB_ERR_INVALID_PARAM; } #else ERROR("Compact-mode extraction (System Compression) " "is only supported on Windows!"); return WIMLIB_ERR_UNSUPPORTED; #endif } if ((extract_flags & (WIMLIB_EXTRACT_FLAG_RPFIX | WIMLIB_EXTRACT_FLAG_NORPFIX | WIMLIB_EXTRACT_FLAG_IMAGEMODE)) == WIMLIB_EXTRACT_FLAG_IMAGEMODE) { /* For full-image extraction, do reparse point fixups by default * if the WIM header says they are enabled. */ if (wim->hdr.flags & WIM_HDR_FLAG_RP_FIX) extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX; } *extract_flags_p = extract_flags; return 0; } struct append_dentry_ctx { struct wim_dentry **dentries; size_t num_dentries; size_t num_alloc_dentries; }; static int append_dentry_cb(struct wim_dentry *dentry, void *_ctx) { struct append_dentry_ctx *ctx = _ctx; if (ctx->num_dentries == ctx->num_alloc_dentries) { struct wim_dentry **new_dentries; size_t new_length; new_length = max(ctx->num_alloc_dentries + 8, ctx->num_alloc_dentries * 3 / 2); new_dentries = REALLOC(ctx->dentries, new_length * sizeof(ctx->dentries[0])); if (new_dentries == NULL) return WIMLIB_ERR_NOMEM; ctx->dentries = new_dentries; ctx->num_alloc_dentries = new_length; } ctx->dentries[ctx->num_dentries++] = dentry; return 0; } /* Append dentries matched by a path which can contain wildcard characters. */ static int append_matched_dentries(WIMStruct *wim, const tchar *orig_pattern, int extract_flags, struct append_dentry_ctx *ctx) { const size_t count_before = ctx->num_dentries; tchar *pattern; int ret; pattern = canonicalize_wim_path(orig_pattern); if (!pattern) return WIMLIB_ERR_NOMEM; ret = expand_path_pattern(wim_get_current_root_dentry(wim), pattern, append_dentry_cb, ctx); FREE(pattern); if (ret || ctx->num_dentries > count_before) return ret; if (extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_GLOB) { ERROR("No matches for path pattern \"%"TS"\"", orig_pattern); return WIMLIB_ERR_PATH_DOES_NOT_EXIST; } WARNING("No matches for path pattern \"%"TS"\"", orig_pattern); return 0; } static int do_wimlib_extract_paths(WIMStruct *wim, int image, const tchar *target, const tchar * const *paths, size_t num_paths, int extract_flags) { int ret; struct wim_dentry **trees; size_t num_trees; if (wim == NULL || target == NULL || target[0] == T('\0') || (num_paths != 0 && paths == NULL)) return WIMLIB_ERR_INVALID_PARAM; ret = check_extract_flags(wim, &extract_flags); if (ret) return ret; ret = select_wim_image(wim, image); if (ret) return ret; ret = wim_checksum_unhashed_blobs(wim); if (ret) return ret; if ((extract_flags & (WIMLIB_EXTRACT_FLAG_NTFS | WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE)) == (WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE)) { ret = mkdir_if_needed(target); if (ret) return ret; } if (extract_flags & WIMLIB_EXTRACT_FLAG_GLOB_PATHS) { struct append_dentry_ctx append_dentry_ctx = { .dentries = NULL, .num_dentries = 0, .num_alloc_dentries = 0, }; for (size_t i = 0; i < num_paths; i++) { ret = append_matched_dentries(wim, paths[i], extract_flags, &append_dentry_ctx); if (ret) { trees = append_dentry_ctx.dentries; goto out_free_trees; } } trees = append_dentry_ctx.dentries; num_trees = append_dentry_ctx.num_dentries; } else { trees = MALLOC(num_paths * sizeof(trees[0])); if (trees == NULL) return WIMLIB_ERR_NOMEM; for (size_t i = 0; i < num_paths; i++) { tchar *path = canonicalize_wim_path(paths[i]); if (path == NULL) { ret = WIMLIB_ERR_NOMEM; goto out_free_trees; } trees[i] = get_dentry(wim, path, WIMLIB_CASE_PLATFORM_DEFAULT); FREE(path); if (trees[i] == NULL) { ERROR("Path \"%"TS"\" does not exist " "in WIM image %d", paths[i], wim->current_image); ret = WIMLIB_ERR_PATH_DOES_NOT_EXIST; goto out_free_trees; } } num_trees = num_paths; } if (num_trees == 0) { ret = 0; goto out_free_trees; } ret = extract_trees(wim, trees, num_trees, target, extract_flags); out_free_trees: FREE(trees); return ret; } static int extract_single_image(WIMStruct *wim, int image, const tchar *target, int extract_flags) { const tchar *path = WIMLIB_WIM_ROOT_PATH; extract_flags |= WIMLIB_EXTRACT_FLAG_IMAGEMODE; return do_wimlib_extract_paths(wim, image, target, &path, 1, extract_flags); } static const tchar * const filename_forbidden_chars = #ifdef __WIN32__ T("<>:\"/\\|?*"); #else T("/"); #endif /* This function checks if it is okay to use a WIM image's name as a directory * name. */ static bool image_name_ok_as_dir(const tchar *image_name) { return image_name && *image_name && !tstrpbrk(image_name, filename_forbidden_chars) && tstrcmp(image_name, T(".")) && tstrcmp(image_name, T("..")) && tstrlen(image_name) <= 128; } /* Extracts all images from the WIM to the directory @target, with the images * placed in subdirectories named by their image names. */ static int extract_all_images(WIMStruct *wim, const tchar *target, int extract_flags) { size_t output_path_len = tstrlen(target); tchar buf[output_path_len + 1 + 128 + 1]; int ret; int image; const tchar *image_name; if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) { ERROR("Cannot extract multiple images in NTFS extraction mode."); return WIMLIB_ERR_INVALID_PARAM; } ret = mkdir_if_needed(target); if (ret) return ret; tmemcpy(buf, target, output_path_len); buf[output_path_len] = OS_PREFERRED_PATH_SEPARATOR; for (image = 1; image <= wim->hdr.image_count; image++) { image_name = wimlib_get_image_name(wim, image); if (image_name_ok_as_dir(image_name)) { tstrcpy(buf + output_path_len + 1, image_name); } else { /* Image name is empty or contains forbidden characters. * Use image number instead. */ tsprintf(buf + output_path_len + 1, T("%d"), image); } ret = extract_single_image(wim, image, buf, extract_flags); if (ret) return ret; } return 0; } static int do_wimlib_extract_image(WIMStruct *wim, int image, const tchar *target, int extract_flags) { if (extract_flags & (WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE | WIMLIB_EXTRACT_FLAG_TO_STDOUT | WIMLIB_EXTRACT_FLAG_GLOB_PATHS)) return WIMLIB_ERR_INVALID_PARAM; if (image == WIMLIB_ALL_IMAGES) return extract_all_images(wim, target, extract_flags); else return extract_single_image(wim, image, target, extract_flags); } /**************************************************************************** * Extraction API * ****************************************************************************/ WIMLIBAPI int wimlib_extract_paths(WIMStruct *wim, int image, const tchar *target, const tchar * const *paths, size_t num_paths, int extract_flags) { if (extract_flags & ~WIMLIB_EXTRACT_MASK_PUBLIC) return WIMLIB_ERR_INVALID_PARAM; return do_wimlib_extract_paths(wim, image, target, paths, num_paths, extract_flags); } WIMLIBAPI int wimlib_extract_pathlist(WIMStruct *wim, int image, const tchar *target, const tchar *path_list_file, int extract_flags) { int ret; tchar **paths; size_t num_paths; void *mem; ret = read_path_list_file(path_list_file, &paths, &num_paths, &mem); if (ret) { ERROR("Failed to read path list file \"%"TS"\"", path_list_file ? path_list_file : T("")); return ret; } ret = wimlib_extract_paths(wim, image, target, (const tchar * const *)paths, num_paths, extract_flags); FREE(paths); FREE(mem); return ret; } WIMLIBAPI int wimlib_extract_image_from_pipe_with_progress(int pipe_fd, const tchar *image_num_or_name, const tchar *target, int extract_flags, wimlib_progress_func_t progfunc, void *progctx) { int ret; WIMStruct *pwm; struct filedes *in_fd; int image; unsigned i; if (extract_flags & ~WIMLIB_EXTRACT_MASK_PUBLIC) return WIMLIB_ERR_INVALID_PARAM; /* Read the WIM header from the pipe and get a WIMStruct to represent * the pipable WIM. Caveats: Unlike getting a WIMStruct with * wimlib_open_wim(), getting a WIMStruct in this way will result in an * empty blob table, no XML data read, and no filename set. */ ret = open_wim_as_WIMStruct(&pipe_fd, WIMLIB_OPEN_FLAG_FROM_PIPE, &pwm, progfunc, progctx); if (ret) return ret; /* Sanity check to make sure this is a pipable WIM. */ if (pwm->hdr.magic != PWM_MAGIC) { ERROR("The WIM being read from file descriptor %d " "is not pipable!", pipe_fd); ret = WIMLIB_ERR_NOT_PIPABLE; goto out_wimlib_free; } /* Sanity check to make sure the first part of a pipable split WIM is * sent over the pipe first. */ if (pwm->hdr.part_number != 1) { ERROR("The first part of the split WIM must be " "sent over the pipe first."); ret = WIMLIB_ERR_INVALID_PIPABLE_WIM; goto out_wimlib_free; } in_fd = &pwm->in_fd; wimlib_assert(in_fd->offset == WIM_HEADER_DISK_SIZE); /* As mentioned, the WIMStruct we created from the pipe does not have * XML data yet. Fix this by reading the extra copy of the XML data * that directly follows the header in pipable WIMs. (Note: see * write_pipable_wim() for more details about the format of pipable * WIMs.) */ { u8 hash[SHA1_HASH_SIZE]; ret = read_pwm_blob_header(pwm, hash, &pwm->hdr.xml_data_reshdr, NULL); if (ret) goto out_wimlib_free; if (!(pwm->hdr.xml_data_reshdr.flags & WIM_RESHDR_FLAG_METADATA)) { ERROR("Expected XML data, but found non-metadata resource."); ret = WIMLIB_ERR_INVALID_PIPABLE_WIM; goto out_wimlib_free; } ret = read_wim_xml_data(pwm); if (ret) goto out_wimlib_free; if (xml_get_image_count(pwm->xml_info) != pwm->hdr.image_count) { ERROR("Image count in XML data is not the same as in WIM header."); ret = WIMLIB_ERR_IMAGE_COUNT; goto out_wimlib_free; } } /* Get image index (this may use the XML data that was just read to * resolve an image name). */ if (image_num_or_name) { image = wimlib_resolve_image(pwm, image_num_or_name); if (image == WIMLIB_NO_IMAGE) { ERROR("\"%"TS"\" is not a valid image in the pipable WIM!", image_num_or_name); ret = WIMLIB_ERR_INVALID_IMAGE; goto out_wimlib_free; } else if (image == WIMLIB_ALL_IMAGES) { ERROR("Applying all images from a pipe is not supported!"); ret = WIMLIB_ERR_INVALID_IMAGE; goto out_wimlib_free; } } else { if (pwm->hdr.image_count != 1) { ERROR("No image was specified, but the pipable WIM " "did not contain exactly 1 image"); ret = WIMLIB_ERR_INVALID_IMAGE; goto out_wimlib_free; } image = 1; } /* Load the needed metadata resource. */ for (i = 1; i <= pwm->hdr.image_count; i++) { ret = handle_pwm_metadata_resource(pwm, i, i == image); if (ret) goto out_wimlib_free; } /* Extract the image. */ extract_flags |= WIMLIB_EXTRACT_FLAG_FROM_PIPE; ret = do_wimlib_extract_image(pwm, image, target, extract_flags); /* Clean up and return. */ out_wimlib_free: wimlib_free(pwm); return ret; } WIMLIBAPI int wimlib_extract_image_from_pipe(int pipe_fd, const tchar *image_num_or_name, const tchar *target, int extract_flags) { return wimlib_extract_image_from_pipe_with_progress(pipe_fd, image_num_or_name, target, extract_flags, NULL, NULL); } WIMLIBAPI int wimlib_extract_image(WIMStruct *wim, int image, const tchar *target, int extract_flags) { if (extract_flags & ~WIMLIB_EXTRACT_MASK_PUBLIC) return WIMLIB_ERR_INVALID_PARAM; return do_wimlib_extract_image(wim, image, target, extract_flags); } wimlib-1.13.1/src/wim.c0000644000175000017500000006611113277475672011561 00000000000000/* * wim.c - High-level code dealing with WIMStructs and images. */ /* * Copyright (C) 2012-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include "wimlib.h" #include "wimlib/assert.h" #include "wimlib/blob_table.h" #include "wimlib/dentry.h" #include "wimlib/encoding.h" #include "wimlib/file_io.h" #include "wimlib/integrity.h" #include "wimlib/metadata.h" #include "wimlib/security.h" #include "wimlib/wim.h" #include "wimlib/xml.h" #include "wimlib/win32.h" /* Information about the available compression types for the WIM format. */ static const struct { const tchar *name; u32 min_chunk_size; u32 max_chunk_size; u32 default_nonsolid_chunk_size; u32 default_solid_chunk_size; } wim_ctype_info[] = { [WIMLIB_COMPRESSION_TYPE_NONE] = { .name = T("None"), .min_chunk_size = 0, .max_chunk_size = 0, .default_nonsolid_chunk_size = 0, .default_solid_chunk_size = 0, }, [WIMLIB_COMPRESSION_TYPE_XPRESS] = { .name = T("XPRESS"), .min_chunk_size = 4096, .max_chunk_size = 65536, .default_nonsolid_chunk_size = 32768, .default_solid_chunk_size = 32768, }, [WIMLIB_COMPRESSION_TYPE_LZX] = { .name = T("LZX"), .min_chunk_size = 32768, .max_chunk_size = 2097152, .default_nonsolid_chunk_size = 32768, .default_solid_chunk_size = 32768, }, [WIMLIB_COMPRESSION_TYPE_LZMS] = { .name = T("LZMS"), .min_chunk_size = 32768, .max_chunk_size = 1073741824, .default_nonsolid_chunk_size = 131072, .default_solid_chunk_size = 67108864, }, }; /* Is the specified compression type valid? */ static bool wim_compression_type_valid(enum wimlib_compression_type ctype) { return (unsigned)ctype < ARRAY_LEN(wim_ctype_info) && wim_ctype_info[(unsigned)ctype].name != NULL; } /* Is the specified chunk size valid for the compression type? */ static bool wim_chunk_size_valid(u32 chunk_size, enum wimlib_compression_type ctype) { if (!(chunk_size == 0 || is_power_of_2(chunk_size))) return false; return chunk_size >= wim_ctype_info[(unsigned)ctype].min_chunk_size && chunk_size <= wim_ctype_info[(unsigned)ctype].max_chunk_size; } /* Return the default chunk size to use for the specified compression type in * non-solid resources. */ static u32 wim_default_nonsolid_chunk_size(enum wimlib_compression_type ctype) { return wim_ctype_info[(unsigned)ctype].default_nonsolid_chunk_size; } /* Return the default chunk size to use for the specified compression type in * solid resources. */ static u32 wim_default_solid_chunk_size(enum wimlib_compression_type ctype) { return wim_ctype_info[(unsigned)ctype].default_solid_chunk_size; } /* Return the default compression type to use in solid resources. */ static enum wimlib_compression_type wim_default_solid_compression_type(void) { return WIMLIB_COMPRESSION_TYPE_LZMS; } static int is_blob_in_solid_resource(struct blob_descriptor *blob, void *_ignore) { return blob->blob_location == BLOB_IN_WIM && (blob->rdesc->flags & WIM_RESHDR_FLAG_SOLID); } bool wim_has_solid_resources(const WIMStruct *wim) { return for_blob_in_table(wim->blob_table, is_blob_in_solid_resource, NULL); } static WIMStruct * new_wim_struct(void) { WIMStruct *wim = CALLOC(1, sizeof(WIMStruct)); if (!wim) return NULL; wim->refcnt = 1; filedes_invalidate(&wim->in_fd); filedes_invalidate(&wim->out_fd); wim->out_solid_compression_type = wim_default_solid_compression_type(); wim->out_solid_chunk_size = wim_default_solid_chunk_size( wim->out_solid_compression_type); return wim; } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_create_new_wim(enum wimlib_compression_type ctype, WIMStruct **wim_ret) { int ret; WIMStruct *wim; ret = wimlib_global_init(0); if (ret) return ret; if (!wim_ret) return WIMLIB_ERR_INVALID_PARAM; if (!wim_compression_type_valid(ctype)) return WIMLIB_ERR_INVALID_COMPRESSION_TYPE; wim = new_wim_struct(); if (!wim) return WIMLIB_ERR_NOMEM; /* Fill in wim->hdr with default values */ wim->hdr.magic = WIM_MAGIC; wim->hdr.wim_version = WIM_VERSION_DEFAULT; wim->hdr.part_number = 1; wim->hdr.total_parts = 1; wim->compression_type = WIMLIB_COMPRESSION_TYPE_NONE; /* Set the output compression type */ wim->out_compression_type = ctype; wim->out_chunk_size = wim_default_nonsolid_chunk_size(ctype); /* Allocate an empty XML info and blob table */ wim->xml_info = xml_new_info_struct(); wim->blob_table = new_blob_table(64); if (!wim->xml_info || !wim->blob_table) { wimlib_free(wim); return WIMLIB_ERR_NOMEM; } *wim_ret = wim; return 0; } static void unload_image_metadata(struct wim_image_metadata *imd) { free_dentry_tree(imd->root_dentry, NULL); imd->root_dentry = NULL; free_wim_security_data(imd->security_data); imd->security_data = NULL; INIT_HLIST_HEAD(&imd->inode_list); } /* Release a reference to the specified image metadata. This assumes that no * WIMStruct has the image selected. */ void put_image_metadata(struct wim_image_metadata *imd) { struct blob_descriptor *blob, *tmp; if (!imd) return; wimlib_assert(imd->refcnt > 0); if (--imd->refcnt != 0) return; wimlib_assert(imd->selected_refcnt == 0); unload_image_metadata(imd); list_for_each_entry_safe(blob, tmp, &imd->unhashed_blobs, unhashed_list) free_blob_descriptor(blob); free_blob_descriptor(imd->metadata_blob); FREE(imd); } /* Appends the specified image metadata structure to the array of image metadata * for a WIM, and increments the image count. */ int append_image_metadata(WIMStruct *wim, struct wim_image_metadata *imd) { struct wim_image_metadata **imd_array; if (!wim_has_metadata(wim)) return WIMLIB_ERR_METADATA_NOT_FOUND; if (wim->hdr.image_count >= MAX_IMAGES) return WIMLIB_ERR_IMAGE_COUNT; imd_array = REALLOC(wim->image_metadata, sizeof(wim->image_metadata[0]) * (wim->hdr.image_count + 1)); if (!imd_array) return WIMLIB_ERR_NOMEM; wim->image_metadata = imd_array; imd_array[wim->hdr.image_count++] = imd; return 0; } static struct wim_image_metadata * new_image_metadata(struct blob_descriptor *metadata_blob, struct wim_security_data *security_data) { struct wim_image_metadata *imd; imd = CALLOC(1, sizeof(*imd)); if (!imd) return NULL; metadata_blob->is_metadata = 1; imd->refcnt = 1; imd->selected_refcnt = 0; imd->root_dentry = NULL; imd->security_data = security_data; imd->metadata_blob = metadata_blob; INIT_HLIST_HEAD(&imd->inode_list); INIT_LIST_HEAD(&imd->unhashed_blobs); imd->stats_outdated = false; return imd; } /* Create an image metadata structure for a new empty image. */ struct wim_image_metadata * new_empty_image_metadata(void) { struct blob_descriptor *metadata_blob; struct wim_security_data *security_data; struct wim_image_metadata *imd; metadata_blob = new_blob_descriptor(); security_data = new_wim_security_data(); if (metadata_blob && security_data) { metadata_blob->refcnt = 1; imd = new_image_metadata(metadata_blob, security_data); if (imd) return imd; } free_blob_descriptor(metadata_blob); FREE(security_data); return NULL; } /* Create an image metadata structure that refers to the specified metadata * resource and is initially not loaded. */ struct wim_image_metadata * new_unloaded_image_metadata(struct blob_descriptor *metadata_blob) { wimlib_assert(metadata_blob->blob_location == BLOB_IN_WIM); return new_image_metadata(metadata_blob, NULL); } /* * Load the metadata for the specified WIM image into memory and set it * as the WIMStruct's currently selected image. * * @wim * The WIMStruct for the WIM. * @image * The 1-based index of the image in the WIM to select. * * On success, 0 will be returned, wim->current_image will be set to * @image, and wim_get_current_image_metadata() can be used to retrieve * metadata information for the image. * * On failure, WIMLIB_ERR_INVALID_IMAGE, WIMLIB_ERR_METADATA_NOT_FOUND, * or another error code will be returned. */ int select_wim_image(WIMStruct *wim, int image) { struct wim_image_metadata *imd; int ret; if (image == WIMLIB_NO_IMAGE) return WIMLIB_ERR_INVALID_IMAGE; if (image == wim->current_image) return 0; if (image < 1 || image > wim->hdr.image_count) return WIMLIB_ERR_INVALID_IMAGE; if (!wim_has_metadata(wim)) return WIMLIB_ERR_METADATA_NOT_FOUND; deselect_current_wim_image(wim); imd = wim->image_metadata[image - 1]; if (!is_image_loaded(imd)) { ret = read_metadata_resource(imd); if (ret) return ret; } wim->current_image = image; imd->selected_refcnt++; return 0; } /* * Deselect the WIMStruct's currently selected image, if any. To reduce memory * usage, possibly unload the newly deselected image's metadata from memory. */ void deselect_current_wim_image(WIMStruct *wim) { struct wim_image_metadata *imd; if (wim->current_image == WIMLIB_NO_IMAGE) return; imd = wim_get_current_image_metadata(wim); wimlib_assert(imd->selected_refcnt > 0); imd->selected_refcnt--; wim->current_image = WIMLIB_NO_IMAGE; if (can_unload_image(imd)) { wimlib_assert(list_empty(&imd->unhashed_blobs)); unload_image_metadata(imd); } } /* * Calls a function on images in the WIM. If @image is WIMLIB_ALL_IMAGES, * @visitor is called on the WIM once for each image, with each image selected * as the current image in turn. If @image is a certain image, @visitor is * called on the WIM only once, with that image selected. */ int for_image(WIMStruct *wim, int image, int (*visitor)(WIMStruct *)) { int ret; int start; int end; int i; if (image == WIMLIB_ALL_IMAGES) { start = 1; end = wim->hdr.image_count; } else if (image >= 1 && image <= wim->hdr.image_count) { start = image; end = image; } else { return WIMLIB_ERR_INVALID_IMAGE; } for (i = start; i <= end; i++) { ret = select_wim_image(wim, i); if (ret != 0) return ret; ret = visitor(wim); if (ret != 0) return ret; } return 0; } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_resolve_image(WIMStruct *wim, const tchar *image_name_or_num) { tchar *p; long image; int i; if (!image_name_or_num || !*image_name_or_num) return WIMLIB_NO_IMAGE; if (!tstrcasecmp(image_name_or_num, T("all")) || !tstrcasecmp(image_name_or_num, T("*"))) return WIMLIB_ALL_IMAGES; image = tstrtol(image_name_or_num, &p, 10); if (p != image_name_or_num && *p == T('\0') && image > 0) { if (image > wim->hdr.image_count) return WIMLIB_NO_IMAGE; return image; } else { for (i = 1; i <= wim->hdr.image_count; i++) { if (!tstrcmp(image_name_or_num, wimlib_get_image_name(wim, i))) return i; } return WIMLIB_NO_IMAGE; } } /* API function documented in wimlib.h */ WIMLIBAPI void wimlib_print_available_images(const WIMStruct *wim, int image) { int first; int last; int i; int n; if (image == WIMLIB_ALL_IMAGES) { n = tprintf(T("Available Images:\n")); first = 1; last = wim->hdr.image_count; } else if (image >= 1 && image <= wim->hdr.image_count) { n = tprintf(T("Information for Image %d\n"), image); first = image; last = image; } else { tprintf(T("wimlib_print_available_images(): Invalid image %d"), image); return; } for (i = 0; i < n - 1; i++) tputchar(T('-')); tputchar(T('\n')); for (i = first; i <= last; i++) xml_print_image_info(wim->xml_info, i); } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_get_wim_info(WIMStruct *wim, struct wimlib_wim_info *info) { memset(info, 0, sizeof(struct wimlib_wim_info)); copy_guid(info->guid, wim->hdr.guid); info->image_count = wim->hdr.image_count; info->boot_index = wim->hdr.boot_idx; info->wim_version = wim->hdr.wim_version; info->chunk_size = wim->chunk_size; info->part_number = wim->hdr.part_number; info->total_parts = wim->hdr.total_parts; info->compression_type = wim->compression_type; info->total_bytes = xml_get_total_bytes(wim->xml_info); info->has_integrity_table = wim_has_integrity_table(wim); info->opened_from_file = (wim->filename != NULL); info->is_readonly = (wim->hdr.flags & WIM_HDR_FLAG_READONLY) || (wim->hdr.total_parts != 1) || (wim->filename && taccess(wim->filename, W_OK)); info->has_rpfix = (wim->hdr.flags & WIM_HDR_FLAG_RP_FIX) != 0; info->is_marked_readonly = (wim->hdr.flags & WIM_HDR_FLAG_READONLY) != 0; info->write_in_progress = (wim->hdr.flags & WIM_HDR_FLAG_WRITE_IN_PROGRESS) != 0; info->metadata_only = (wim->hdr.flags & WIM_HDR_FLAG_METADATA_ONLY) != 0; info->resource_only = (wim->hdr.flags & WIM_HDR_FLAG_RESOURCE_ONLY) != 0; info->spanned = (wim->hdr.flags & WIM_HDR_FLAG_SPANNED) != 0; info->pipable = wim_is_pipable(wim); return 0; } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_set_wim_info(WIMStruct *wim, const struct wimlib_wim_info *info, int which) { if (which & ~(WIMLIB_CHANGE_READONLY_FLAG | WIMLIB_CHANGE_GUID | WIMLIB_CHANGE_BOOT_INDEX | WIMLIB_CHANGE_RPFIX_FLAG)) return WIMLIB_ERR_INVALID_PARAM; if ((which & WIMLIB_CHANGE_BOOT_INDEX) && info->boot_index > wim->hdr.image_count) return WIMLIB_ERR_INVALID_IMAGE; if (which & WIMLIB_CHANGE_READONLY_FLAG) { if (info->is_marked_readonly) wim->hdr.flags |= WIM_HDR_FLAG_READONLY; else wim->hdr.flags &= ~WIM_HDR_FLAG_READONLY; } if (which & WIMLIB_CHANGE_GUID) copy_guid(wim->hdr.guid, info->guid); if (which & WIMLIB_CHANGE_BOOT_INDEX) wim->hdr.boot_idx = info->boot_index; if (which & WIMLIB_CHANGE_RPFIX_FLAG) { if (info->has_rpfix) wim->hdr.flags |= WIM_HDR_FLAG_RP_FIX; else wim->hdr.flags &= ~WIM_HDR_FLAG_RP_FIX; } return 0; } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_set_output_compression_type(WIMStruct *wim, enum wimlib_compression_type ctype) { if (!wim_compression_type_valid(ctype)) return WIMLIB_ERR_INVALID_COMPRESSION_TYPE; wim->out_compression_type = ctype; /* Reset the chunk size if it's no longer valid. */ if (!wim_chunk_size_valid(wim->out_chunk_size, ctype)) wim->out_chunk_size = wim_default_nonsolid_chunk_size(ctype); return 0; } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_set_output_pack_compression_type(WIMStruct *wim, enum wimlib_compression_type ctype) { if (!wim_compression_type_valid(ctype)) return WIMLIB_ERR_INVALID_COMPRESSION_TYPE; /* Solid resources can't be uncompressed. */ if (ctype == WIMLIB_COMPRESSION_TYPE_NONE) return WIMLIB_ERR_INVALID_COMPRESSION_TYPE; wim->out_solid_compression_type = ctype; /* Reset the chunk size if it's no longer valid. */ if (!wim_chunk_size_valid(wim->out_solid_chunk_size, ctype)) wim->out_solid_chunk_size = wim_default_solid_chunk_size(ctype); return 0; } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_set_output_chunk_size(WIMStruct *wim, u32 chunk_size) { if (chunk_size == 0) { wim->out_chunk_size = wim_default_nonsolid_chunk_size(wim->out_compression_type); return 0; } if (!wim_chunk_size_valid(chunk_size, wim->out_compression_type)) return WIMLIB_ERR_INVALID_CHUNK_SIZE; wim->out_chunk_size = chunk_size; return 0; } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_set_output_pack_chunk_size(WIMStruct *wim, u32 chunk_size) { if (chunk_size == 0) { wim->out_solid_chunk_size = wim_default_solid_chunk_size(wim->out_solid_compression_type); return 0; } if (!wim_chunk_size_valid(chunk_size, wim->out_solid_compression_type)) return WIMLIB_ERR_INVALID_CHUNK_SIZE; wim->out_solid_chunk_size = chunk_size; return 0; } /* API function documented in wimlib.h */ WIMLIBAPI const tchar * wimlib_get_compression_type_string(enum wimlib_compression_type ctype) { if (!wim_compression_type_valid(ctype)) return T("Invalid"); return wim_ctype_info[(unsigned)ctype].name; } WIMLIBAPI void wimlib_register_progress_function(WIMStruct *wim, wimlib_progress_func_t progfunc, void *progctx) { wim->progfunc = progfunc; wim->progctx = progctx; } static int open_wim_file(const tchar *filename, struct filedes *fd_ret) { int raw_fd; raw_fd = topen(filename, O_RDONLY | O_BINARY); if (raw_fd < 0) { ERROR_WITH_ERRNO("Can't open \"%"TS"\" read-only", filename); return WIMLIB_ERR_OPEN; } filedes_init(fd_ret, raw_fd); return 0; } /* * Begins the reading of a WIM file; opens the file and reads its header and * blob table, and optionally checks the integrity. */ static int begin_read(WIMStruct *wim, const void *wim_filename_or_fd, int open_flags) { int ret; const tchar *wimfile; if (open_flags & WIMLIB_OPEN_FLAG_FROM_PIPE) { wimfile = NULL; filedes_init(&wim->in_fd, *(const int*)wim_filename_or_fd); wim->in_fd.is_pipe = 1; } else { wimfile = wim_filename_or_fd; ret = open_wim_file(wimfile, &wim->in_fd); if (ret) return ret; /* The absolute path to the WIM is requested so that * wimlib_overwrite() still works even if the process changes * its working directory. This actually happens if a WIM is * mounted read-write, since the FUSE thread changes directory * to "/", and it needs to be able to find the WIM file again. * * This will break if the full path to the WIM changes in the * intervening time... * * Warning: in Windows native builds, realpath() calls the * replacement function in win32_replacements.c. */ wim->filename = realpath(wimfile, NULL); if (!wim->filename) { ERROR_WITH_ERRNO("Failed to get full path to file " "\"%"TS"\"", wimfile); if (errno == ENOMEM) return WIMLIB_ERR_NOMEM; else return WIMLIB_ERR_NO_FILENAME; } } ret = read_wim_header(wim, &wim->hdr); if (ret) return ret; if (wim->hdr.flags & WIM_HDR_FLAG_WRITE_IN_PROGRESS) { WARNING("The WIM_HDR_FLAG_WRITE_IN_PROGRESS flag is set in the header of\n" " \"%"TS"\". It may be being changed by another process,\n" " or a process may have crashed while writing the WIM.", wimfile); } if (open_flags & WIMLIB_OPEN_FLAG_WRITE_ACCESS) { ret = can_modify_wim(wim); if (ret) return ret; } if ((open_flags & WIMLIB_OPEN_FLAG_ERROR_IF_SPLIT) && (wim->hdr.total_parts != 1)) return WIMLIB_ERR_IS_SPLIT_WIM; /* If the boot index is invalid, print a warning and set it to 0 */ if (wim->hdr.boot_idx > wim->hdr.image_count) { WARNING("Ignoring invalid boot index."); wim->hdr.boot_idx = 0; } /* Check and cache the compression type */ if (wim->hdr.flags & WIM_HDR_FLAG_COMPRESSION) { if (wim->hdr.flags & WIM_HDR_FLAG_COMPRESS_LZX) { wim->compression_type = WIMLIB_COMPRESSION_TYPE_LZX; } else if (wim->hdr.flags & (WIM_HDR_FLAG_COMPRESS_XPRESS | WIM_HDR_FLAG_COMPRESS_XPRESS_2)) { wim->compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS; } else if (wim->hdr.flags & WIM_HDR_FLAG_COMPRESS_LZMS) { wim->compression_type = WIMLIB_COMPRESSION_TYPE_LZMS; } else { return WIMLIB_ERR_INVALID_COMPRESSION_TYPE; } } else { wim->compression_type = WIMLIB_COMPRESSION_TYPE_NONE; } wim->out_compression_type = wim->compression_type; /* Check and cache the chunk size. */ wim->chunk_size = wim->hdr.chunk_size; wim->out_chunk_size = wim->chunk_size; if (!wim_chunk_size_valid(wim->chunk_size, wim->compression_type)) { ERROR("Invalid chunk size (%"PRIu32" bytes) " "for compression type %"TS"!", wim->chunk_size, wimlib_get_compression_type_string(wim->compression_type)); return WIMLIB_ERR_INVALID_CHUNK_SIZE; } if (open_flags & WIMLIB_OPEN_FLAG_CHECK_INTEGRITY) { ret = check_wim_integrity(wim); if (ret == WIM_INTEGRITY_NONEXISTENT) { WARNING("\"%"TS"\" does not contain integrity " "information. Skipping integrity check.", wimfile); } else if (ret == WIM_INTEGRITY_NOT_OK) { return WIMLIB_ERR_INTEGRITY; } else if (ret != WIM_INTEGRITY_OK) { return ret; } } if (wim->hdr.image_count != 0 && wim->hdr.part_number == 1) { wim->image_metadata = CALLOC(wim->hdr.image_count, sizeof(wim->image_metadata[0])); if (!wim->image_metadata) return WIMLIB_ERR_NOMEM; } if (open_flags & WIMLIB_OPEN_FLAG_FROM_PIPE) { wim->blob_table = new_blob_table(64); if (!wim->blob_table) return WIMLIB_ERR_NOMEM; } else { if (wim->hdr.blob_table_reshdr.uncompressed_size == 0 && wim->hdr.xml_data_reshdr.uncompressed_size == 0) return WIMLIB_ERR_WIM_IS_INCOMPLETE; ret = read_wim_xml_data(wim); if (ret) return ret; if (xml_get_image_count(wim->xml_info) != wim->hdr.image_count) { ERROR("The WIM's header is inconsistent with its XML data.\n" " Please submit a bug report if you believe this " "WIM file should be considered valid."); return WIMLIB_ERR_IMAGE_COUNT; } ret = read_blob_table(wim); if (ret) return ret; } return 0; } int open_wim_as_WIMStruct(const void *wim_filename_or_fd, int open_flags, WIMStruct **wim_ret, wimlib_progress_func_t progfunc, void *progctx) { WIMStruct *wim; int ret; ret = wimlib_global_init(0); if (ret) return ret; wim = new_wim_struct(); if (!wim) return WIMLIB_ERR_NOMEM; wim->progfunc = progfunc; wim->progctx = progctx; ret = begin_read(wim, wim_filename_or_fd, open_flags); if (ret) { wimlib_free(wim); return ret; } *wim_ret = wim; return 0; } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_open_wim_with_progress(const tchar *wimfile, int open_flags, WIMStruct **wim_ret, wimlib_progress_func_t progfunc, void *progctx) { if (open_flags & ~(WIMLIB_OPEN_FLAG_CHECK_INTEGRITY | WIMLIB_OPEN_FLAG_ERROR_IF_SPLIT | WIMLIB_OPEN_FLAG_WRITE_ACCESS)) return WIMLIB_ERR_INVALID_PARAM; if (!wimfile || !*wimfile || !wim_ret) return WIMLIB_ERR_INVALID_PARAM; return open_wim_as_WIMStruct(wimfile, open_flags, wim_ret, progfunc, progctx); } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_open_wim(const tchar *wimfile, int open_flags, WIMStruct **wim_ret) { return wimlib_open_wim_with_progress(wimfile, open_flags, wim_ret, NULL, NULL); } /* Checksum all blobs that are unhashed (other than the metadata blobs), merging * them into the blob table as needed. This is a no-op unless files have been * added to an image in the same WIMStruct. */ int wim_checksum_unhashed_blobs(WIMStruct *wim) { int ret; if (!wim_has_metadata(wim)) return 0; for (int i = 0; i < wim->hdr.image_count; i++) { struct blob_descriptor *blob, *tmp; struct wim_image_metadata *imd = wim->image_metadata[i]; image_for_each_unhashed_blob_safe(blob, tmp, imd) { struct blob_descriptor *new_blob; ret = hash_unhashed_blob(blob, wim->blob_table, &new_blob); if (ret) return ret; if (new_blob != blob) free_blob_descriptor(blob); } } return 0; } /* * can_modify_wim - Check if a given WIM is writeable. This is only the case if * it meets the following three conditions: * * 1. Write access is allowed to the underlying file (if any) at the filesystem level. * 2. The WIM is not part of a spanned set. * 3. The WIM_HDR_FLAG_READONLY flag is not set in the WIM header. * * Return value is 0 if writable; WIMLIB_ERR_WIM_IS_READONLY otherwise. */ int can_modify_wim(WIMStruct *wim) { if (wim->filename) { if (taccess(wim->filename, W_OK)) { ERROR_WITH_ERRNO("Can't modify \"%"TS"\"", wim->filename); return WIMLIB_ERR_WIM_IS_READONLY; } } if (wim->hdr.total_parts != 1) { ERROR("Cannot modify \"%"TS"\": is part of a split WIM", wim->filename); return WIMLIB_ERR_WIM_IS_READONLY; } if (wim->hdr.flags & WIM_HDR_FLAG_READONLY) { ERROR("Cannot modify \"%"TS"\": is marked read-only", wim->filename); return WIMLIB_ERR_WIM_IS_READONLY; } return 0; } /* Release a reference to a WIMStruct. If the reference count reaches 0, the * WIMStruct is freed. */ void wim_decrement_refcnt(WIMStruct *wim) { wimlib_assert(wim->refcnt > 0); if (--wim->refcnt != 0) return; if (filedes_valid(&wim->in_fd)) filedes_close(&wim->in_fd); if (filedes_valid(&wim->out_fd)) filedes_close(&wim->out_fd); wimlib_free_decompressor(wim->decompressor); xml_free_info_struct(wim->xml_info); FREE(wim->filename); FREE(wim); } /* API function documented in wimlib.h */ WIMLIBAPI void wimlib_free(WIMStruct *wim) { if (!wim) return; /* The blob table and image metadata are freed immediately, but other * members of the WIMStruct such as the input file descriptor are * retained until no more exported resources reference the WIMStruct. */ free_blob_table(wim->blob_table); wim->blob_table = NULL; if (wim->image_metadata != NULL) { deselect_current_wim_image(wim); for (int i = 0; i < wim->hdr.image_count; i++) put_image_metadata(wim->image_metadata[i]); FREE(wim->image_metadata); wim->image_metadata = NULL; } wim_decrement_refcnt(wim); } /* API function documented in wimlib.h */ WIMLIBAPI u32 wimlib_get_version(void) { return (WIMLIB_MAJOR_VERSION << 20) | (WIMLIB_MINOR_VERSION << 10) | WIMLIB_PATCH_VERSION; } WIMLIBAPI const tchar * wimlib_get_version_string(void) { return T(PACKAGE_VERSION); } static bool lib_initialized = false; static pthread_mutex_t lib_initialization_mutex = PTHREAD_MUTEX_INITIALIZER; /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_global_init(int init_flags) { int ret = 0; if (lib_initialized) goto out; pthread_mutex_lock(&lib_initialization_mutex); if (lib_initialized) goto out_unlock; #ifdef ENABLE_ERROR_MESSAGES if (!wimlib_error_file) wimlib_error_file = stderr; #endif ret = WIMLIB_ERR_INVALID_PARAM; if (init_flags & ~(WIMLIB_INIT_FLAG_ASSUME_UTF8 | WIMLIB_INIT_FLAG_DONT_ACQUIRE_PRIVILEGES | WIMLIB_INIT_FLAG_STRICT_CAPTURE_PRIVILEGES | WIMLIB_INIT_FLAG_STRICT_APPLY_PRIVILEGES | WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE | WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE)) goto out_unlock; ret = WIMLIB_ERR_INVALID_PARAM; if ((init_flags & (WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE | WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE)) == (WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE | WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE)) goto out_unlock; xml_global_init(); #ifdef __WIN32__ ret = win32_global_init(init_flags); if (ret) goto out_unlock; #endif init_upcase(); if (init_flags & WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE) default_ignore_case = false; else if (init_flags & WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE) default_ignore_case = true; lib_initialized = true; ret = 0; out_unlock: pthread_mutex_unlock(&lib_initialization_mutex); out: return ret; } /* API function documented in wimlib.h */ WIMLIBAPI void wimlib_global_cleanup(void) { if (!lib_initialized) return; pthread_mutex_lock(&lib_initialization_mutex); if (!lib_initialized) goto out_unlock; xml_global_cleanup(); #ifdef __WIN32__ win32_global_cleanup(); #endif wimlib_set_error_file(NULL); lib_initialized = false; out_unlock: pthread_mutex_unlock(&lib_initialization_mutex); } wimlib-1.13.1/src/win32_capture.c0000644000175000017500000025455313376042233013442 00000000000000/* * win32_capture.c - Windows-specific code for capturing files into a WIM image. * * This now uses the native Windows NT API a lot and not just Win32. */ /* * Copyright (C) 2013-2018 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef __WIN32__ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib/win32_common.h" #include "wimlib/assert.h" #include "wimlib/blob_table.h" #include "wimlib/dentry.h" #include "wimlib/encoding.h" #include "wimlib/endianness.h" #include "wimlib/error.h" #include "wimlib/object_id.h" #include "wimlib/paths.h" #include "wimlib/reparse.h" #include "wimlib/scan.h" #include "wimlib/win32_vss.h" #include "wimlib/wof.h" #include "wimlib/xattr.h" struct winnt_scan_ctx { struct scan_params *params; bool is_ntfs; u32 vol_flags; unsigned long num_get_sd_access_denied; unsigned long num_get_sacl_priv_notheld; /* True if WOF is definitely not attached to the volume being scanned; * false if it may be */ bool wof_not_attached; /* A reference to the VSS snapshot being used, or NULL if none */ struct vss_snapshot *snapshot; }; static inline const wchar_t * printable_path(const struct winnt_scan_ctx *ctx) { /* Skip over \\?\ or \??\ */ return ctx->params->cur_path + 4; } /* Description of where data is located on a Windows filesystem */ struct windows_file { /* Is the data the raw encrypted data of an EFS-encrypted file? */ u64 is_encrypted : 1; /* Is this file "open by file ID" rather than the regular "open by * path"? "Open by file ID" uses resources more efficiently. */ u64 is_file_id : 1; /* The file's LCN (logical cluster number) for sorting, or 0 if unknown. */ u64 sort_key : 62; /* Length of the path in bytes, excluding the null terminator if * present. */ size_t path_nbytes; /* A reference to the VSS snapshot containing the file, or NULL if none. */ struct vss_snapshot *snapshot; /* The path to the file. If 'is_encrypted=0' this is an NT namespace * path; if 'is_encrypted=1' this is a Win32 namespace path. If * 'is_file_id=0', then the path is null-terminated. If 'is_file_id=1' * (only allowed with 'is_encrypted=0') the path ends with a binary file * ID and may not be null-terminated. */ wchar_t path[0]; }; /* Allocate a structure to describe the location of a data stream by path. */ static struct windows_file * alloc_windows_file(const wchar_t *path, size_t path_nchars, const wchar_t *stream_name, size_t stream_name_nchars, struct vss_snapshot *snapshot, bool is_encrypted) { size_t full_path_nbytes; struct windows_file *file; wchar_t *p; full_path_nbytes = path_nchars * sizeof(wchar_t); if (stream_name_nchars) full_path_nbytes += (1 + stream_name_nchars) * sizeof(wchar_t); file = MALLOC(sizeof(struct windows_file) + full_path_nbytes + sizeof(wchar_t)); if (!file) return NULL; file->is_encrypted = is_encrypted; file->is_file_id = 0; file->sort_key = 0; file->path_nbytes = full_path_nbytes; file->snapshot = vss_get_snapshot(snapshot); p = wmempcpy(file->path, path, path_nchars); if (stream_name_nchars) { /* Named data stream */ *p++ = L':'; p = wmempcpy(p, stream_name, stream_name_nchars); } *p = L'\0'; return file; } /* Allocate a structure to describe the location of a file by ID. */ static struct windows_file * alloc_windows_file_for_file_id(u64 file_id, const wchar_t *root_path, size_t root_path_nchars, struct vss_snapshot *snapshot) { size_t full_path_nbytes; struct windows_file *file; wchar_t *p; full_path_nbytes = (root_path_nchars * sizeof(wchar_t)) + sizeof(file_id); file = MALLOC(sizeof(struct windows_file) + full_path_nbytes + sizeof(wchar_t)); if (!file) return NULL; file->is_encrypted = 0; file->is_file_id = 1; file->sort_key = 0; file->path_nbytes = full_path_nbytes; file->snapshot = vss_get_snapshot(snapshot); p = wmempcpy(file->path, root_path, root_path_nchars); p = mempcpy(p, &file_id, sizeof(file_id)); *p = L'\0'; return file; } /* Add a stream, located on a Windows filesystem, to the specified WIM inode. */ static int add_stream(struct wim_inode *inode, struct windows_file *windows_file, u64 stream_size, int stream_type, const utf16lechar *stream_name, struct list_head *unhashed_blobs) { struct blob_descriptor *blob = NULL; struct wim_inode_stream *strm; int ret; if (!windows_file) goto err_nomem; /* If the stream is nonempty, create a blob descriptor for it. */ if (stream_size) { blob = new_blob_descriptor(); if (!blob) goto err_nomem; blob->windows_file = windows_file; blob->blob_location = BLOB_IN_WINDOWS_FILE; blob->file_inode = inode; blob->size = stream_size; windows_file = NULL; } strm = inode_add_stream(inode, stream_type, stream_name, blob); if (!strm) goto err_nomem; prepare_unhashed_blob(blob, inode, strm->stream_id, unhashed_blobs); ret = 0; out: if (windows_file) free_windows_file(windows_file); return ret; err_nomem: free_blob_descriptor(blob); ret = WIMLIB_ERR_NOMEM; goto out; } struct windows_file * clone_windows_file(const struct windows_file *file) { struct windows_file *new; new = memdup(file, sizeof(*file) + file->path_nbytes + sizeof(wchar_t)); if (new) vss_get_snapshot(new->snapshot); return new; } void free_windows_file(struct windows_file *file) { vss_put_snapshot(file->snapshot); FREE(file); } int cmp_windows_files(const struct windows_file *file1, const struct windows_file *file2) { /* Compare by starting LCN (logical cluster number) */ int v = cmp_u64(file1->sort_key, file2->sort_key); if (v) return v; /* Fall back to comparing files by path (arbitrary heuristic). */ v = memcmp(file1->path, file2->path, min(file1->path_nbytes, file2->path_nbytes)); if (v) return v; return cmp_u32(file1->path_nbytes, file2->path_nbytes); } const wchar_t * get_windows_file_path(const struct windows_file *file) { return file->path; } /* * Open the file named by the NT namespace path @path of length @path_nchars * characters. If @cur_dir is not NULL then the path is given relative to * @cur_dir; otherwise the path is absolute. @perms is the access mask of * permissions to request on the handle. SYNCHRONIZE permision is always added. */ static NTSTATUS winnt_openat(HANDLE cur_dir, const wchar_t *path, size_t path_nchars, ACCESS_MASK perms, HANDLE *h_ret) { UNICODE_STRING name = { .Length = path_nchars * sizeof(wchar_t), .MaximumLength = path_nchars * sizeof(wchar_t), .Buffer = (wchar_t *)path, }; OBJECT_ATTRIBUTES attr = { .Length = sizeof(attr), .RootDirectory = cur_dir, .ObjectName = &name, }; IO_STATUS_BLOCK iosb; NTSTATUS status; ULONG options = FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT; perms |= SYNCHRONIZE; if (perms & (FILE_READ_DATA | FILE_LIST_DIRECTORY)) { options |= FILE_SYNCHRONOUS_IO_NONALERT; options |= FILE_SEQUENTIAL_ONLY; } retry: status = NtOpenFile(h_ret, perms, &attr, &iosb, FILE_SHARE_VALID_FLAGS, options); if (!NT_SUCCESS(status)) { /* Try requesting fewer permissions */ if (status == STATUS_ACCESS_DENIED || status == STATUS_PRIVILEGE_NOT_HELD) { if (perms & ACCESS_SYSTEM_SECURITY) { perms &= ~ACCESS_SYSTEM_SECURITY; goto retry; } if (perms & READ_CONTROL) { perms &= ~READ_CONTROL; goto retry; } } } return status; } static NTSTATUS winnt_open(const wchar_t *path, size_t path_nchars, ACCESS_MASK perms, HANDLE *h_ret) { return winnt_openat(NULL, path, path_nchars, perms, h_ret); } static const wchar_t * windows_file_to_string(const struct windows_file *file, u8 *buf, size_t bufsize) { if (file->is_file_id) { u64 file_id; memcpy(&file_id, (u8 *)file->path + file->path_nbytes - sizeof(file_id), sizeof(file_id)); swprintf((wchar_t *)buf, L"NTFS inode 0x%016"PRIx64, file_id); } else if (file->path_nbytes + 3 * sizeof(wchar_t) <= bufsize) { swprintf((wchar_t *)buf, L"\"%ls\"", file->path); } else { return L"(name too long)"; } return (wchar_t *)buf; } static int read_winnt_stream_prefix(const struct windows_file *file, u64 size, const struct consume_chunk_callback *cb) { IO_STATUS_BLOCK iosb; UNICODE_STRING name = { .Buffer = (wchar_t *)file->path, .Length = file->path_nbytes, .MaximumLength = file->path_nbytes, }; OBJECT_ATTRIBUTES attr = { .Length = sizeof(attr), .ObjectName = &name, }; HANDLE h; NTSTATUS status; u8 buf[BUFFER_SIZE] _aligned_attribute(8); u64 bytes_remaining; int ret; status = NtOpenFile(&h, FILE_READ_DATA | SYNCHRONIZE, &attr, &iosb, FILE_SHARE_VALID_FLAGS, FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT | FILE_SEQUENTIAL_ONLY | (file->is_file_id ? FILE_OPEN_BY_FILE_ID : 0)); if (unlikely(!NT_SUCCESS(status))) { if (status == STATUS_SHARING_VIOLATION) { ERROR("Can't open %ls for reading:\n" " File is in use by another process! " "Consider using snapshot (VSS) mode.", windows_file_to_string(file, buf, sizeof(buf))); } else { winnt_error(status, L"Can't open %ls for reading", windows_file_to_string(file, buf, sizeof(buf))); } return WIMLIB_ERR_OPEN; } ret = 0; bytes_remaining = size; while (bytes_remaining) { IO_STATUS_BLOCK iosb; ULONG count; ULONG bytes_read; const unsigned max_tries = 5; unsigned tries_remaining = max_tries; count = min(sizeof(buf), bytes_remaining); retry_read: status = NtReadFile(h, NULL, NULL, NULL, &iosb, buf, count, NULL, NULL); if (unlikely(!NT_SUCCESS(status))) { if (status == STATUS_END_OF_FILE) { ERROR("%ls: File was concurrently truncated", windows_file_to_string(file, buf, sizeof(buf))); ret = WIMLIB_ERR_CONCURRENT_MODIFICATION_DETECTED; } else { winnt_warning(status, L"Error reading data from %ls", windows_file_to_string(file, buf, sizeof(buf))); /* Currently these retries are purely a guess; * there is no reproducible problem that they solve. */ if (--tries_remaining) { int delay = 100; if (status == STATUS_INSUFFICIENT_RESOURCES || status == STATUS_NO_MEMORY) { delay *= 25; } WARNING("Retrying after %dms...", delay); Sleep(delay); goto retry_read; } ERROR("Too many retries; returning failure"); ret = WIMLIB_ERR_READ; } break; } else if (unlikely(tries_remaining != max_tries)) { WARNING("A read request had to be retried multiple times " "before it succeeded!"); } bytes_read = iosb.Information; bytes_remaining -= bytes_read; ret = consume_chunk(cb, buf, bytes_read); if (ret) break; } NtClose(h); return ret; } struct win32_encrypted_read_ctx { const struct consume_chunk_callback *cb; int wimlib_err_code; u64 bytes_remaining; }; static DWORD WINAPI win32_encrypted_export_cb(unsigned char *data, void *_ctx, unsigned long len) { struct win32_encrypted_read_ctx *ctx = _ctx; int ret; size_t bytes_to_consume = min(len, ctx->bytes_remaining); if (bytes_to_consume == 0) return ERROR_SUCCESS; ret = consume_chunk(ctx->cb, data, bytes_to_consume); if (ret) { ctx->wimlib_err_code = ret; /* It doesn't matter what error code is returned here, as long * as it isn't ERROR_SUCCESS. */ return ERROR_READ_FAULT; } ctx->bytes_remaining -= bytes_to_consume; return ERROR_SUCCESS; } static int read_win32_encrypted_file_prefix(const wchar_t *path, bool is_dir, u64 size, const struct consume_chunk_callback *cb) { struct win32_encrypted_read_ctx export_ctx; DWORD err; void *file_ctx; int ret; DWORD flags = 0; if (is_dir) flags |= CREATE_FOR_DIR; export_ctx.cb = cb; export_ctx.wimlib_err_code = 0; export_ctx.bytes_remaining = size; err = OpenEncryptedFileRaw(path, flags, &file_ctx); if (err != ERROR_SUCCESS) { win32_error(err, L"Failed to open encrypted file \"%ls\" for raw read", path); return WIMLIB_ERR_OPEN; } err = ReadEncryptedFileRaw(win32_encrypted_export_cb, &export_ctx, file_ctx); if (err != ERROR_SUCCESS) { ret = export_ctx.wimlib_err_code; if (ret == 0) { win32_error(err, L"Failed to read encrypted file \"%ls\"", path); ret = WIMLIB_ERR_READ; } } else if (export_ctx.bytes_remaining != 0) { ERROR("Only could read %"PRIu64" of %"PRIu64" bytes from " "encrypted file \"%ls\"", size - export_ctx.bytes_remaining, size, path); ret = WIMLIB_ERR_READ; } else { ret = 0; } CloseEncryptedFileRaw(file_ctx); return ret; } /* Read the first @size bytes from the file, or named data stream of a file, * described by @blob. */ int read_windows_file_prefix(const struct blob_descriptor *blob, u64 size, const struct consume_chunk_callback *cb) { const struct windows_file *file = blob->windows_file; if (unlikely(file->is_encrypted)) { bool is_dir = (blob->file_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY); return read_win32_encrypted_file_prefix(file->path, is_dir, size, cb); } return read_winnt_stream_prefix(file, size, cb); } /* * Load the short name of a file into a WIM dentry. */ static noinline_for_stack NTSTATUS winnt_get_short_name(HANDLE h, struct wim_dentry *dentry) { /* It's not any harder to just make the NtQueryInformationFile() system * call ourselves, and it saves a dumb call to FindFirstFile() which of * course has to create its own handle. */ NTSTATUS status; IO_STATUS_BLOCK iosb; u8 buf[128] _aligned_attribute(8); const FILE_NAME_INFORMATION *info; status = NtQueryInformationFile(h, &iosb, buf, sizeof(buf), FileAlternateNameInformation); info = (const FILE_NAME_INFORMATION *)buf; if (NT_SUCCESS(status) && info->FileNameLength != 0) { dentry->d_short_name = utf16le_dupz(info->FileName, info->FileNameLength); if (!dentry->d_short_name) return STATUS_NO_MEMORY; dentry->d_short_name_nbytes = info->FileNameLength; } return status; } /* * Load the security descriptor of a file into the corresponding inode and the * WIM image's security descriptor set. */ static noinline_for_stack int winnt_load_security_descriptor(HANDLE h, struct wim_inode *inode, struct winnt_scan_ctx *ctx) { SECURITY_INFORMATION requestedInformation; u8 _buf[4096] _aligned_attribute(8); u8 *buf; ULONG bufsize; ULONG len_needed; NTSTATUS status; /* * LABEL_SECURITY_INFORMATION is needed on Windows Vista and 7 because * Microsoft decided to add mandatory integrity labels to the SACL but * not have them returned by SACL_SECURITY_INFORMATION. * * BACKUP_SECURITY_INFORMATION is needed on Windows 8 because Microsoft * decided to add even more stuff to the SACL and still not have it * returned by SACL_SECURITY_INFORMATION; but they did remember that * backup applications exist and simply want to read the stupid thing * once and for all, so they added a flag to read the entire security * descriptor. * * Older versions of Windows tolerate these new flags being passed in. */ requestedInformation = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION | BACKUP_SECURITY_INFORMATION; buf = _buf; bufsize = sizeof(_buf); /* * We need the file's security descriptor in * SECURITY_DESCRIPTOR_RELATIVE format, and we currently have a handle * opened with as many relevant permissions as possible. At this point, * on Windows there are a number of options for reading a file's * security descriptor: * * GetFileSecurity(): This takes in a path and returns the * SECURITY_DESCRIPTOR_RELATIVE. Problem: this uses an internal handle, * not ours, and the handle created internally doesn't specify * FILE_FLAG_BACKUP_SEMANTICS. Therefore there can be access denied * errors on some files and directories, even when running as the * Administrator. * * GetSecurityInfo(): This takes in a handle and returns the security * descriptor split into a bunch of different parts. This should work, * but it's dumb because we have to put the security descriptor back * together again. * * BackupRead(): This can read the security descriptor, but this is a * difficult-to-use API, probably only works as the Administrator, and * the format of the returned data is not well documented. * * NtQuerySecurityObject(): This is exactly what we need, as it takes * in a handle and returns the security descriptor in * SECURITY_DESCRIPTOR_RELATIVE format. Only problem is that it's a * ntdll function and therefore not officially part of the Win32 API. * Oh well. */ while (!NT_SUCCESS(status = NtQuerySecurityObject(h, requestedInformation, (PSECURITY_DESCRIPTOR)buf, bufsize, &len_needed))) { switch (status) { case STATUS_BUFFER_TOO_SMALL: wimlib_assert(buf == _buf); buf = MALLOC(len_needed); if (!buf) { status = STATUS_NO_MEMORY; goto out; } bufsize = len_needed; break; case STATUS_PRIVILEGE_NOT_HELD: case STATUS_ACCESS_DENIED: if (ctx->params->add_flags & WIMLIB_ADD_FLAG_STRICT_ACLS) { default: /* Permission denied in STRICT_ACLS mode, or * unknown error. */ goto out; } if (requestedInformation & SACL_SECURITY_INFORMATION) { /* Try again without the SACL. */ ctx->num_get_sacl_priv_notheld++; requestedInformation &= ~(SACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION | BACKUP_SECURITY_INFORMATION); break; } /* Fake success (useful when capturing as * non-Administrator). */ ctx->num_get_sd_access_denied++; status = STATUS_SUCCESS; goto out; } } /* We can get a length of 0 with Samba. Assume that means "no security * descriptor". */ if (len_needed == 0) goto out; /* Add the security descriptor to the WIM image, and save its ID in * the file's inode. */ inode->i_security_id = sd_set_add_sd(ctx->params->sd_set, buf, len_needed); if (unlikely(inode->i_security_id < 0)) status = STATUS_NO_MEMORY; out: if (unlikely(buf != _buf)) FREE(buf); if (!NT_SUCCESS(status)) { winnt_error(status, L"\"%ls\": Can't read security descriptor", printable_path(ctx)); return WIMLIB_ERR_STAT; } return 0; } /* Load a file's object ID into the corresponding WIM inode. */ static noinline_for_stack int winnt_load_object_id(HANDLE h, struct wim_inode *inode, struct winnt_scan_ctx *ctx) { FILE_OBJECTID_BUFFER buffer; NTSTATUS status; u32 len; if (!(ctx->vol_flags & FILE_SUPPORTS_OBJECT_IDS)) return 0; status = winnt_fsctl(h, FSCTL_GET_OBJECT_ID, NULL, 0, &buffer, sizeof(buffer), &len); if (status == STATUS_OBJECTID_NOT_FOUND) /* No object ID */ return 0; if (status == STATUS_INVALID_DEVICE_REQUEST || status == STATUS_NOT_SUPPORTED /* Samba volume, WinXP */) { /* The filesystem claimed to support object IDs, but we can't * actually read them. This happens with Samba. */ ctx->vol_flags &= ~FILE_SUPPORTS_OBJECT_IDS; return 0; } if (!NT_SUCCESS(status)) { winnt_error(status, L"\"%ls\": Can't read object ID", printable_path(ctx)); return WIMLIB_ERR_STAT; } if (len == 0) /* No object ID (for directories) */ return 0; if (!inode_set_object_id(inode, &buffer, len)) return WIMLIB_ERR_NOMEM; return 0; } /* Load a file's extended attributes into the corresponding WIM inode. */ static noinline_for_stack int winnt_load_xattrs(HANDLE h, struct wim_inode *inode, struct winnt_scan_ctx *ctx, u32 ea_size) { IO_STATUS_BLOCK iosb; NTSTATUS status; u8 _buf[1024] _aligned_attribute(4); u8 *buf = _buf; const FILE_FULL_EA_INFORMATION *ea; struct wim_xattr_entry *entry; int ret; /* * EaSize from FILE_EA_INFORMATION is apparently supposed to give the * size of the buffer required for NtQueryEaFile(), but it doesn't * actually work correctly; it can be off by about 4 bytes per xattr. * * So just start out by doubling the advertised size, and also handle * STATUS_BUFFER_OVERFLOW just in case. */ retry: if (unlikely(ea_size * 2 < ea_size)) ea_size = UINT32_MAX; else ea_size *= 2; if (unlikely(ea_size > sizeof(_buf))) { buf = MALLOC(ea_size); if (!buf) { if (ea_size >= (1 << 20)) { WARNING("\"%ls\": EaSize was extremely large (%u)", printable_path(ctx), ea_size); } return WIMLIB_ERR_NOMEM; } } status = NtQueryEaFile(h, &iosb, buf, ea_size, FALSE, NULL, 0, NULL, TRUE); if (unlikely(!NT_SUCCESS(status))) { if (status == STATUS_BUFFER_OVERFLOW) { if (buf != _buf) { FREE(buf); buf = NULL; } goto retry; } if (status == STATUS_NO_EAS_ON_FILE) { /* * FILE_EA_INFORMATION.EaSize was nonzero so this * shouldn't happen, but just in case... */ ret = 0; goto out; } winnt_error(status, L"\"%ls\": Can't read extended attributes", printable_path(ctx)); ret = WIMLIB_ERR_STAT; goto out; } ea = (const FILE_FULL_EA_INFORMATION *)buf; entry = (struct wim_xattr_entry *)buf; for (;;) { /* * wim_xattr_entry is not larger than FILE_FULL_EA_INFORMATION, * so we can reuse the same buffer by overwriting the * FILE_FULL_EA_INFORMATION with the wim_xattr_entry in-place. */ FILE_FULL_EA_INFORMATION _ea; STATIC_ASSERT(offsetof(struct wim_xattr_entry, name) <= offsetof(FILE_FULL_EA_INFORMATION, EaName)); wimlib_assert((u8 *)entry <= (const u8 *)ea); memcpy(&_ea, ea, sizeof(_ea)); entry->value_len = cpu_to_le16(_ea.EaValueLength); entry->name_len = _ea.EaNameLength; entry->flags = _ea.Flags; memmove(entry->name, ea->EaName, _ea.EaNameLength); entry->name[_ea.EaNameLength] = '\0'; memmove(&entry->name[_ea.EaNameLength + 1], &ea->EaName[_ea.EaNameLength + 1], _ea.EaValueLength); entry = (struct wim_xattr_entry *) &entry->name[_ea.EaNameLength + 1 + _ea.EaValueLength]; if (_ea.NextEntryOffset == 0) break; ea = (const FILE_FULL_EA_INFORMATION *) ((const u8 *)ea + _ea.NextEntryOffset); } wimlib_assert((u8 *)entry - buf <= ea_size); ret = WIMLIB_ERR_NOMEM; if (!inode_set_xattrs(inode, buf, (u8 *)entry - buf)) goto out; ret = 0; out: if (unlikely(buf != _buf)) FREE(buf); return ret; } static int winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret, HANDLE cur_dir, const wchar_t *relative_path, size_t relative_path_nchars, const wchar_t *filename, struct winnt_scan_ctx *ctx); static int winnt_recurse_directory(HANDLE h, struct wim_dentry *parent, struct winnt_scan_ctx *ctx) { void *buf; const size_t bufsize = 8192; IO_STATUS_BLOCK iosb; NTSTATUS status; int ret; buf = MALLOC(bufsize); if (!buf) return WIMLIB_ERR_NOMEM; /* Using NtQueryDirectoryFile() we can re-use the same open handle, * which we opened with FILE_FLAG_BACKUP_SEMANTICS. */ while (NT_SUCCESS(status = NtQueryDirectoryFile(h, NULL, NULL, NULL, &iosb, buf, bufsize, FileNamesInformation, FALSE, NULL, FALSE))) { const FILE_NAMES_INFORMATION *info = buf; for (;;) { if (!should_ignore_filename(info->FileName, info->FileNameLength / 2)) { struct wim_dentry *child; size_t orig_path_nchars; const wchar_t *filename; ret = WIMLIB_ERR_NOMEM; filename = pathbuf_append_name(ctx->params, info->FileName, info->FileNameLength / 2, &orig_path_nchars); if (!filename) goto out_free_buf; ret = winnt_build_dentry_tree_recursive( &child, h, filename, info->FileNameLength / 2, filename, ctx); pathbuf_truncate(ctx->params, orig_path_nchars); if (ret) goto out_free_buf; attach_scanned_tree(parent, child, ctx->params->blob_table); } if (info->NextEntryOffset == 0) break; info = (const FILE_NAMES_INFORMATION *) ((const u8 *)info + info->NextEntryOffset); } } if (unlikely(status != STATUS_NO_MORE_FILES)) { winnt_error(status, L"\"%ls\": Can't read directory", printable_path(ctx)); ret = WIMLIB_ERR_READ; } out_free_buf: FREE(buf); return ret; } /* Reparse point fixup status code */ #define RP_FIXED (-1) static bool file_has_ino_and_dev(HANDLE h, u64 ino, u64 dev) { NTSTATUS status; IO_STATUS_BLOCK iosb; FILE_INTERNAL_INFORMATION int_info; FILE_FS_VOLUME_INFORMATION vol_info; status = NtQueryInformationFile(h, &iosb, &int_info, sizeof(int_info), FileInternalInformation); if (!NT_SUCCESS(status)) return false; if (int_info.IndexNumber.QuadPart != ino) return false; status = NtQueryVolumeInformationFile(h, &iosb, &vol_info, sizeof(vol_info), FileFsVolumeInformation); if (!(NT_SUCCESS(status) || status == STATUS_BUFFER_OVERFLOW)) return false; if (iosb.Information < offsetof(FILE_FS_VOLUME_INFORMATION, VolumeSerialNumber) + sizeof(vol_info.VolumeSerialNumber)) return false; return (vol_info.VolumeSerialNumber == dev); } /* * This is the Windows equivalent of unix_relativize_link_target(); see there * for general details. This version works with an "absolute" Windows link * target, specified from the root of the Windows kernel object namespace. Note * that we have to open directories with a trailing slash when present because * \??\E: opens the E: device itself and not the filesystem root directory. */ static const wchar_t * winnt_relativize_link_target(const wchar_t *target, size_t target_nbytes, u64 ino, u64 dev) { UNICODE_STRING name; OBJECT_ATTRIBUTES attr; IO_STATUS_BLOCK iosb; NTSTATUS status; const wchar_t *target_end; const wchar_t *p; target_end = target + (target_nbytes / sizeof(wchar_t)); /* Empty path??? */ if (target_end == target) return target; /* No leading slash??? */ if (target[0] != L'\\') return target; /* UNC path??? */ if ((target_end - target) >= 2 && target[0] == L'\\' && target[1] == L'\\') return target; attr.Length = sizeof(attr); attr.RootDirectory = NULL; attr.ObjectName = &name; attr.Attributes = 0; attr.SecurityDescriptor = NULL; attr.SecurityQualityOfService = NULL; name.Buffer = (wchar_t *)target; name.Length = 0; p = target; do { HANDLE h; const wchar_t *orig_p = p; /* Skip non-backslashes */ while (p != target_end && *p != L'\\') p++; /* Skip backslashes */ while (p != target_end && *p == L'\\') p++; /* Append path component */ name.Length += (p - orig_p) * sizeof(wchar_t); name.MaximumLength = name.Length; /* Try opening the file */ status = NtOpenFile(&h, FILE_READ_ATTRIBUTES | FILE_TRAVERSE, &attr, &iosb, FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT); if (NT_SUCCESS(status)) { /* Reset root directory */ if (attr.RootDirectory) NtClose(attr.RootDirectory); attr.RootDirectory = h; name.Buffer = (wchar_t *)p; name.Length = 0; if (file_has_ino_and_dev(h, ino, dev)) goto out_close_root_dir; } } while (p != target_end); p = target; out_close_root_dir: if (attr.RootDirectory) NtClose(attr.RootDirectory); while (p > target && *(p - 1) == L'\\') p--; return p; } static int winnt_rpfix_progress(struct scan_params *params, const struct link_reparse_point *link, int scan_status) { size_t print_name_nchars = link->print_name_nbytes / sizeof(wchar_t); wchar_t print_name0[print_name_nchars + 1]; wmemcpy(print_name0, link->print_name, print_name_nchars); print_name0[print_name_nchars] = L'\0'; params->progress.scan.symlink_target = print_name0; return do_scan_progress(params, scan_status, NULL); } static int winnt_try_rpfix(struct reparse_buffer_disk *rpbuf, u16 *rpbuflen_p, struct scan_params *params) { struct link_reparse_point link; const wchar_t *rel_target; int ret; if (parse_link_reparse_point(rpbuf, *rpbuflen_p, &link)) { /* Couldn't understand the reparse data; don't do the fixup. */ return 0; } /* * Don't do reparse point fixups on relative symbolic links. * * On Windows, a relative symbolic link is supposed to be identifiable * by having reparse tag WIM_IO_REPARSE_TAG_SYMLINK and flags * SYMBOLIC_LINK_RELATIVE. We will use this information, although this * may not always do what the user expects, since drive-relative * symbolic links such as "\Users\Public" have SYMBOLIC_LINK_RELATIVE * set, in addition to truly relative symbolic links such as "Users" or * "Users\Public". However, WIMGAPI (as of Windows 8.1) has this same * behavior. * * Otherwise, as far as I can tell, the targets of symbolic links that * are NOT relative, as well as junctions (note: a mountpoint is the * sames thing as a junction), must be NT namespace paths, for example: * * - \??\e:\Users\Public * - \DosDevices\e:\Users\Public * - \Device\HardDiskVolume4\Users\Public * - \??\Volume{c47cb07c-946e-4155-b8f7-052e9cec7628}\Users\Public * - \DosDevices\Volume{c47cb07c-946e-4155-b8f7-052e9cec7628}\Users\Public */ if (link_is_relative_symlink(&link)) return 0; rel_target = winnt_relativize_link_target(link.substitute_name, link.substitute_name_nbytes, params->capture_root_ino, params->capture_root_dev); if (rel_target == link.substitute_name) { /* Target points outside of the tree being captured or had an * unrecognized path format. Don't adjust it. */ return winnt_rpfix_progress(params, &link, WIMLIB_SCAN_DENTRY_NOT_FIXED_SYMLINK); } /* We have an absolute target pointing within the directory being * captured. @rel_target is the suffix of the link target that is the * part relative to the directory being captured. * * We will cut off the prefix before this part (which is the path to the * directory being captured) and add a dummy prefix. Since the process * will need to be reversed when applying the image, it doesn't matter * what exactly the prefix is, as long as it looks like an absolute * path. */ static const wchar_t prefix[6] = L"\\??\\X:"; static const size_t num_unprintable_chars = 4; size_t rel_target_nbytes = link.substitute_name_nbytes - ((const u8 *)rel_target - (const u8 *)link.substitute_name); wchar_t tmp[(sizeof(prefix) + rel_target_nbytes) / sizeof(wchar_t)]; memcpy(tmp, prefix, sizeof(prefix)); memcpy(tmp + ARRAY_LEN(prefix), rel_target, rel_target_nbytes); link.substitute_name = tmp; link.substitute_name_nbytes = sizeof(tmp); link.print_name = link.substitute_name + num_unprintable_chars; link.print_name_nbytes = link.substitute_name_nbytes - (num_unprintable_chars * sizeof(wchar_t)); if (make_link_reparse_point(&link, rpbuf, rpbuflen_p)) return 0; ret = winnt_rpfix_progress(params, &link, WIMLIB_SCAN_DENTRY_FIXED_SYMLINK); if (ret) return ret; return RP_FIXED; } /* Load the reparse data of a file into the corresponding WIM inode. If the * reparse point is a symbolic link or junction with an absolute target and * RPFIX mode is enabled, then also rewrite its target to be relative to the * capture root. */ static noinline_for_stack int winnt_load_reparse_data(HANDLE h, struct wim_inode *inode, struct winnt_scan_ctx *ctx) { struct reparse_buffer_disk rpbuf; NTSTATUS status; u32 len; u16 rpbuflen; int ret; if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) { /* See comment above assign_stream_types_encrypted() */ WARNING("Ignoring reparse data of encrypted file \"%ls\"", printable_path(ctx)); return 0; } status = winnt_fsctl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, &rpbuf, sizeof(rpbuf), &len); if (!NT_SUCCESS(status)) { winnt_error(status, L"\"%ls\": Can't get reparse point", printable_path(ctx)); return WIMLIB_ERR_READLINK; } rpbuflen = len; if (unlikely(rpbuflen < REPARSE_DATA_OFFSET)) { ERROR("\"%ls\": reparse point buffer is too short", printable_path(ctx)); return WIMLIB_ERR_INVALID_REPARSE_DATA; } if (le32_to_cpu(rpbuf.rptag) == WIM_IO_REPARSE_TAG_DEDUP) { /* * Windows treats Data Deduplication reparse points specially. * Reads from the unnamed data stream actually return the * redirected file contents, even with FILE_OPEN_REPARSE_POINT. * Deduplicated files also cannot be properly restored without * also restoring the "System Volume Information" directory, * which wimlib excludes by default. Therefore, the logical * behavior for us seems to be to ignore the reparse point and * treat the file as a normal file. */ inode->i_attributes &= ~FILE_ATTRIBUTE_REPARSE_POINT; return 0; } if (ctx->params->add_flags & WIMLIB_ADD_FLAG_RPFIX) { ret = winnt_try_rpfix(&rpbuf, &rpbuflen, ctx->params); if (ret == RP_FIXED) inode->i_rp_flags &= ~WIM_RP_FLAG_NOT_FIXED; else if (ret) return ret; } inode->i_reparse_tag = le32_to_cpu(rpbuf.rptag); inode->i_rp_reserved = le16_to_cpu(rpbuf.rpreserved); if (!inode_add_stream_with_data(inode, STREAM_TYPE_REPARSE_POINT, NO_STREAM_NAME, rpbuf.rpdata, rpbuflen - REPARSE_DATA_OFFSET, ctx->params->blob_table)) return WIMLIB_ERR_NOMEM; return 0; } static DWORD WINAPI win32_tally_encrypted_size_cb(unsigned char *_data, void *_size_ret, unsigned long len) { *(u64*)_size_ret += len; return ERROR_SUCCESS; } static int win32_get_encrypted_file_size(const wchar_t *path, bool is_dir, u64 *size_ret) { DWORD err; void *file_ctx; int ret; DWORD flags = 0; if (is_dir) flags |= CREATE_FOR_DIR; err = OpenEncryptedFileRaw(path, flags, &file_ctx); if (err != ERROR_SUCCESS) { win32_error(err, L"Failed to open encrypted file \"%ls\" for raw read", path); return WIMLIB_ERR_OPEN; } *size_ret = 0; err = ReadEncryptedFileRaw(win32_tally_encrypted_size_cb, size_ret, file_ctx); if (err != ERROR_SUCCESS) { win32_error(err, L"Failed to read raw encrypted data from \"%ls\"", path); ret = WIMLIB_ERR_READ; } else { ret = 0; } CloseEncryptedFileRaw(file_ctx); return ret; } static int winnt_scan_efsrpc_raw_data(struct wim_inode *inode, struct winnt_scan_ctx *ctx) { wchar_t *path = ctx->params->cur_path; size_t path_nchars = ctx->params->cur_path_nchars; const bool is_dir = (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY); struct windows_file *windows_file; u64 size; int ret; /* OpenEncryptedFileRaw() expects a Win32 name. */ wimlib_assert(!wmemcmp(path, L"\\??\\", 4)); path[1] = L'\\'; ret = win32_get_encrypted_file_size(path, is_dir, &size); if (ret) goto out; /* Empty EFSRPC data does not make sense */ wimlib_assert(size != 0); windows_file = alloc_windows_file(path, path_nchars, NULL, 0, ctx->snapshot, true); ret = add_stream(inode, windows_file, size, STREAM_TYPE_EFSRPC_RAW_DATA, NO_STREAM_NAME, ctx->params->unhashed_blobs); out: path[1] = L'?'; return ret; } static bool get_data_stream_name(const wchar_t *raw_stream_name, size_t raw_stream_name_nchars, const wchar_t **stream_name_ret, size_t *stream_name_nchars_ret) { const wchar_t *sep, *type, *end; /* The stream name should be returned as :NAME:TYPE */ if (raw_stream_name_nchars < 1) return false; if (raw_stream_name[0] != L':') return false; raw_stream_name++; raw_stream_name_nchars--; end = raw_stream_name + raw_stream_name_nchars; sep = wmemchr(raw_stream_name, L':', raw_stream_name_nchars); if (!sep) return false; type = sep + 1; if (end - type != 5) return false; if (wmemcmp(type, L"$DATA", 5)) return false; *stream_name_ret = raw_stream_name; *stream_name_nchars_ret = sep - raw_stream_name; return true; } static int winnt_scan_data_stream(wchar_t *raw_stream_name, size_t raw_stream_name_nchars, u64 stream_size, struct wim_inode *inode, struct winnt_scan_ctx *ctx) { wchar_t *stream_name; size_t stream_name_nchars; struct windows_file *windows_file; /* Given the raw stream name (which is something like * :streamname:$DATA), extract just the stream name part (streamname). * Ignore any non-$DATA streams. */ if (!get_data_stream_name(raw_stream_name, raw_stream_name_nchars, (const wchar_t **)&stream_name, &stream_name_nchars)) return 0; stream_name[stream_name_nchars] = L'\0'; windows_file = alloc_windows_file(ctx->params->cur_path, ctx->params->cur_path_nchars, stream_name, stream_name_nchars, ctx->snapshot, false); return add_stream(inode, windows_file, stream_size, STREAM_TYPE_DATA, stream_name, ctx->params->unhashed_blobs); } /* * Load information about the data streams of an open file into a WIM inode. * * We use the NtQueryInformationFile() system call instead of FindFirstStream() * and FindNextStream(). This is done for two reasons: * * - FindFirstStream() opens its own handle to the file or directory and * apparently does so without specifying FILE_FLAG_BACKUP_SEMANTICS, thereby * causing access denied errors on certain files (even when running as the * Administrator). * - FindFirstStream() and FindNextStream() is only available on Windows Vista * and later, whereas the stream support in NtQueryInformationFile() was * already present in Windows XP. */ static noinline_for_stack int winnt_scan_data_streams(HANDLE h, struct wim_inode *inode, u64 file_size, struct winnt_scan_ctx *ctx) { int ret; u8 _buf[4096] _aligned_attribute(8); u8 *buf; size_t bufsize; IO_STATUS_BLOCK iosb; NTSTATUS status; FILE_STREAM_INFORMATION *info; buf = _buf; bufsize = sizeof(_buf); if (!(ctx->vol_flags & FILE_NAMED_STREAMS)) goto unnamed_only; /* Get a buffer containing the stream information. */ while (!NT_SUCCESS(status = NtQueryInformationFile(h, &iosb, buf, bufsize, FileStreamInformation))) { switch (status) { case STATUS_BUFFER_OVERFLOW: { u8 *newbuf; bufsize *= 2; if (buf == _buf) newbuf = MALLOC(bufsize); else newbuf = REALLOC(buf, bufsize); if (!newbuf) { ret = WIMLIB_ERR_NOMEM; goto out_free_buf; } buf = newbuf; } break; case STATUS_NOT_IMPLEMENTED: case STATUS_NOT_SUPPORTED: case STATUS_INVALID_INFO_CLASS: goto unnamed_only; default: winnt_error(status, L"\"%ls\": Failed to query stream information", printable_path(ctx)); ret = WIMLIB_ERR_READ; goto out_free_buf; } } if (iosb.Information == 0) { /* No stream information. */ ret = 0; goto out_free_buf; } /* Parse one or more stream information structures. */ info = (FILE_STREAM_INFORMATION *)buf; for (;;) { /* Load the stream information. */ ret = winnt_scan_data_stream(info->StreamName, info->StreamNameLength / 2, info->StreamSize.QuadPart, inode, ctx); if (ret) goto out_free_buf; if (info->NextEntryOffset == 0) { /* No more stream information. */ break; } /* Advance to next stream information. */ info = (FILE_STREAM_INFORMATION *) ((u8 *)info + info->NextEntryOffset); } ret = 0; goto out_free_buf; unnamed_only: /* The volume does not support named streams. Only capture the unnamed * data stream. */ if (inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)) { ret = 0; goto out_free_buf; } { wchar_t stream_name[] = L"::$DATA"; ret = winnt_scan_data_stream(stream_name, 7, file_size, inode, ctx); } out_free_buf: /* Free buffer if allocated on heap. */ if (unlikely(buf != _buf)) FREE(buf); return ret; } static u64 extract_starting_lcn(const RETRIEVAL_POINTERS_BUFFER *extents) { if (extents->ExtentCount < 1) return 0; return extents->Extents[0].Lcn.QuadPart; } static noinline_for_stack u64 get_sort_key(HANDLE h) { STARTING_VCN_INPUT_BUFFER in = { .StartingVcn.QuadPart = 0 }; RETRIEVAL_POINTERS_BUFFER out; if (!NT_SUCCESS(winnt_fsctl(h, FSCTL_GET_RETRIEVAL_POINTERS, &in, sizeof(in), &out, sizeof(out), NULL))) return 0; return extract_starting_lcn(&out); } static void set_sort_key(struct wim_inode *inode, u64 sort_key) { for (unsigned i = 0; i < inode->i_num_streams; i++) { struct wim_inode_stream *strm = &inode->i_streams[i]; struct blob_descriptor *blob = stream_blob_resolved(strm); if (blob && blob->blob_location == BLOB_IN_WINDOWS_FILE) blob->windows_file->sort_key = sort_key; } } static inline bool should_try_to_use_wimboot_hash(const struct wim_inode *inode, const struct winnt_scan_ctx *ctx) { /* Directories and encrypted files aren't valid for external backing. */ if (inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ENCRYPTED)) return false; /* If the file is a reparse point, then try the hash fixup if it's a WOF * reparse point and we're in WIMBOOT mode. Otherwise, try the hash * fixup if WOF may be attached. */ if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) return (inode->i_reparse_tag == WIM_IO_REPARSE_TAG_WOF) && (ctx->params->add_flags & WIMLIB_ADD_FLAG_WIMBOOT); return !ctx->wof_not_attached; } /* * This function implements an optimization for capturing files from a * filesystem with a backing WIM(s). If a file is WIM-backed, then we can * retrieve the SHA-1 message digest of its original contents from its reparse * point. This may eliminate the need to read the file's data and/or allow the * file's data to be immediately deduplicated with existing data in the WIM. * * If WOF is attached, then this function is merely an optimization, but * potentially a very effective one. If WOF is detached, then this function * really causes WIM-backed files to be, effectively, automatically * "dereferenced" when possible; the unnamed data stream is updated to reference * the original contents and the reparse point is removed. * * This function returns 0 if the fixup succeeded or was intentionally not * executed. Otherwise it returns an error code. */ static noinline_for_stack int try_to_use_wimboot_hash(HANDLE h, struct wim_inode *inode, struct winnt_scan_ctx *ctx) { struct blob_table *blob_table = ctx->params->blob_table; struct wim_inode_stream *reparse_strm = NULL; struct wim_inode_stream *strm; struct blob_descriptor *blob; u8 hash[SHA1_HASH_SIZE]; int ret; if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) { struct reparse_buffer_disk rpbuf; struct { struct wof_external_info wof_info; struct wim_provider_rpdata wim_info; } *rpdata = (void *)rpbuf.rpdata; struct blob_descriptor *reparse_blob; /* The file has a WOF reparse point, so WOF must be detached. * We can read the reparse point directly. */ ctx->wof_not_attached = true; reparse_strm = inode_get_unnamed_stream(inode, STREAM_TYPE_REPARSE_POINT); reparse_blob = stream_blob_resolved(reparse_strm); if (!reparse_blob || reparse_blob->size < sizeof(*rpdata)) return 0; /* Not a WIM-backed file */ ret = read_blob_into_buf(reparse_blob, rpdata); if (ret) return ret; if (rpdata->wof_info.version != WOF_CURRENT_VERSION || rpdata->wof_info.provider != WOF_PROVIDER_WIM || rpdata->wim_info.version != 2) return 0; /* Not a WIM-backed file */ /* Okay, this is a WIM backed file. Get its SHA-1 hash. */ copy_hash(hash, rpdata->wim_info.unnamed_data_stream_hash); } else { struct { struct wof_external_info wof_info; struct wim_provider_external_info wim_info; } out; NTSTATUS status; /* WOF may be attached. Try reading this file's external * backing info. */ status = winnt_fsctl(h, FSCTL_GET_EXTERNAL_BACKING, NULL, 0, &out, sizeof(out), NULL); /* Is WOF not attached? */ if (status == STATUS_INVALID_DEVICE_REQUEST || status == STATUS_NOT_SUPPORTED) { ctx->wof_not_attached = true; return 0; } /* Is this file not externally backed? */ if (status == STATUS_OBJECT_NOT_EXTERNALLY_BACKED) return 0; /* Does this file have an unknown type of external backing that * needed a larger information buffer? */ if (status == STATUS_BUFFER_TOO_SMALL) return 0; /* Was there some other failure? */ if (status != STATUS_SUCCESS) { winnt_error(status, L"\"%ls\": FSCTL_GET_EXTERNAL_BACKING failed", printable_path(ctx)); return WIMLIB_ERR_STAT; } /* Is this file backed by a WIM? */ if (out.wof_info.version != WOF_CURRENT_VERSION || out.wof_info.provider != WOF_PROVIDER_WIM || out.wim_info.version != WIM_PROVIDER_CURRENT_VERSION) return 0; /* Okay, this is a WIM backed file. Get its SHA-1 hash. */ copy_hash(hash, out.wim_info.unnamed_data_stream_hash); } /* If the file's unnamed data stream is nonempty, then fill in its hash * and deduplicate it if possible. * * With WOF detached, we require that the blob *must* de-duplicable for * any action can be taken, since without WOF we can't fall back to * getting the "dereferenced" data by reading the stream (the real * stream is sparse and contains all zeroes). */ strm = inode_get_unnamed_data_stream(inode); if (strm && (blob = stream_blob_resolved(strm))) { struct blob_descriptor **back_ptr; if (reparse_strm && !lookup_blob(blob_table, hash)) return 0; back_ptr = retrieve_pointer_to_unhashed_blob(blob); copy_hash(blob->hash, hash); if (after_blob_hashed(blob, back_ptr, blob_table) != blob) free_blob_descriptor(blob); } /* Remove the reparse point, if present. */ if (reparse_strm) { inode_remove_stream(inode, reparse_strm, blob_table); inode->i_attributes &= ~(FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_SPARSE_FILE); if (inode->i_attributes == 0) inode->i_attributes = FILE_ATTRIBUTE_NORMAL; } return 0; } struct file_info { u32 attributes; u32 num_links; u64 creation_time; u64 last_write_time; u64 last_access_time; u64 ino; u64 end_of_file; u32 ea_size; }; static noinline_for_stack NTSTATUS get_file_info(HANDLE h, struct file_info *info) { IO_STATUS_BLOCK iosb; NTSTATUS status; FILE_ALL_INFORMATION all_info; status = NtQueryInformationFile(h, &iosb, &all_info, sizeof(all_info), FileAllInformation); if (unlikely(!NT_SUCCESS(status) && status != STATUS_BUFFER_OVERFLOW)) return status; info->attributes = all_info.BasicInformation.FileAttributes; info->num_links = all_info.StandardInformation.NumberOfLinks; info->creation_time = all_info.BasicInformation.CreationTime.QuadPart; info->last_write_time = all_info.BasicInformation.LastWriteTime.QuadPart; info->last_access_time = all_info.BasicInformation.LastAccessTime.QuadPart; info->ino = all_info.InternalInformation.IndexNumber.QuadPart; info->end_of_file = all_info.StandardInformation.EndOfFile.QuadPart; info->ea_size = all_info.EaInformation.EaSize; return STATUS_SUCCESS; } static void get_volume_information(HANDLE h, struct winnt_scan_ctx *ctx) { u8 _attr_info[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 128] _aligned_attribute(8); FILE_FS_ATTRIBUTE_INFORMATION *attr_info = (void *)_attr_info; FILE_FS_VOLUME_INFORMATION vol_info; struct file_info file_info; IO_STATUS_BLOCK iosb; NTSTATUS status; /* Get volume flags */ status = NtQueryVolumeInformationFile(h, &iosb, attr_info, sizeof(_attr_info), FileFsAttributeInformation); if (NT_SUCCESS(status)) { ctx->vol_flags = attr_info->FileSystemAttributes; ctx->is_ntfs = (attr_info->FileSystemNameLength == 4 * sizeof(wchar_t)) && !wmemcmp(attr_info->FileSystemName, L"NTFS", 4); } else { winnt_warning(status, L"\"%ls\": Can't get volume attributes", printable_path(ctx)); } /* Get volume ID. */ status = NtQueryVolumeInformationFile(h, &iosb, &vol_info, sizeof(vol_info), FileFsVolumeInformation); if ((NT_SUCCESS(status) || status == STATUS_BUFFER_OVERFLOW) && (iosb.Information >= offsetof(FILE_FS_VOLUME_INFORMATION, VolumeSerialNumber) + sizeof(vol_info.VolumeSerialNumber))) { ctx->params->capture_root_dev = vol_info.VolumeSerialNumber; } else { winnt_warning(status, L"\"%ls\": Can't get volume ID", printable_path(ctx)); } /* Get inode number. */ status = get_file_info(h, &file_info); if (NT_SUCCESS(status)) { ctx->params->capture_root_ino = file_info.ino; } else { winnt_warning(status, L"\"%ls\": Can't get file information", printable_path(ctx)); } } static int winnt_build_dentry_tree_recursive(struct wim_dentry **root_ret, HANDLE cur_dir, const wchar_t *relative_path, size_t relative_path_nchars, const wchar_t *filename, struct winnt_scan_ctx *ctx) { struct wim_dentry *root = NULL; struct wim_inode *inode = NULL; HANDLE h = NULL; int ret; NTSTATUS status; struct file_info file_info; u64 sort_key; ret = try_exclude(ctx->params); if (unlikely(ret < 0)) /* Excluded? */ goto out_progress; if (unlikely(ret > 0)) /* Error? */ goto out; /* Open the file with permission to read metadata. Although we will * later need a handle with FILE_LIST_DIRECTORY permission (or, * equivalently, FILE_READ_DATA; they're the same numeric value) if the * file is a directory, it can significantly slow things down to request * this permission on all nondirectories. Perhaps it causes Windows to * start prefetching the file contents... */ status = winnt_openat(cur_dir, relative_path, relative_path_nchars, FILE_READ_ATTRIBUTES | FILE_READ_EA | READ_CONTROL | ACCESS_SYSTEM_SECURITY, &h); if (unlikely(!NT_SUCCESS(status))) { if (status == STATUS_DELETE_PENDING) { WARNING("\"%ls\": Deletion pending; skipping file", printable_path(ctx)); ret = 0; goto out; } if (status == STATUS_SHARING_VIOLATION) { ERROR("Can't open \"%ls\":\n" " File is in use by another process! " "Consider using snapshot (VSS) mode.", printable_path(ctx)); ret = WIMLIB_ERR_OPEN; goto out; } winnt_error(status, L"\"%ls\": Can't open file", printable_path(ctx)); if (status == STATUS_FVE_LOCKED_VOLUME) ret = WIMLIB_ERR_FVE_LOCKED_VOLUME; else ret = WIMLIB_ERR_OPEN; goto out; } /* Get information about the file. */ status = get_file_info(h, &file_info); if (!NT_SUCCESS(status)) { winnt_error(status, L"\"%ls\": Can't get file information", printable_path(ctx)); ret = WIMLIB_ERR_STAT; goto out; } /* Create a WIM dentry with an associated inode, which may be shared. * * However, we need to explicitly check for directories and files with * only 1 link and refuse to hard link them. This is because Windows * has a bug where it can return duplicate File IDs for files and * directories on the FAT filesystem. * * Since we don't follow mount points on Windows, we don't need to query * the volume ID per-file. Just once, for the root, is enough. But we * can't simply pass 0, because then there could be inode collisions * among multiple calls to win32_build_dentry_tree() that are scanning * files on different volumes. */ ret = inode_table_new_dentry(ctx->params->inode_table, filename, file_info.ino, ctx->params->capture_root_dev, (file_info.num_links <= 1), &root); if (ret) goto out; /* Get the short (DOS) name of the file. */ status = winnt_get_short_name(h, root); /* If we can't read the short filename for any reason other than * out-of-memory, just ignore the error and assume the file has no short * name. This shouldn't be an issue, since the short names are * essentially obsolete anyway. */ if (unlikely(status == STATUS_NO_MEMORY)) { ret = WIMLIB_ERR_NOMEM; goto out; } inode = root->d_inode; if (inode->i_nlink > 1) { /* Shared inode (hard link); skip reading per-inode information. */ goto out_progress; } inode->i_attributes = file_info.attributes; inode->i_creation_time = file_info.creation_time; inode->i_last_write_time = file_info.last_write_time; inode->i_last_access_time = file_info.last_access_time; /* Get the file's security descriptor, unless we are capturing in * NO_ACLS mode or the volume does not support security descriptors. */ if (!(ctx->params->add_flags & WIMLIB_ADD_FLAG_NO_ACLS) && (ctx->vol_flags & FILE_PERSISTENT_ACLS)) { ret = winnt_load_security_descriptor(h, inode, ctx); if (ret) goto out; } /* Get the file's object ID. */ ret = winnt_load_object_id(h, inode, ctx); if (ret) goto out; /* Get the file's extended attributes. */ if (unlikely(file_info.ea_size != 0)) { ret = winnt_load_xattrs(h, inode, ctx, file_info.ea_size); if (ret) goto out; } /* If this is a reparse point, load the reparse data. */ if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)) { ret = winnt_load_reparse_data(h, inode, ctx); if (ret) goto out; } sort_key = get_sort_key(h); if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)) { /* Load information about the raw encrypted data. This is * needed for any directory or non-directory that has * FILE_ATTRIBUTE_ENCRYPTED set. * * Note: since OpenEncryptedFileRaw() fails with * ERROR_SHARING_VIOLATION if there are any open handles to the * file, we have to close the file and re-open it later if * needed. */ NtClose(h); h = NULL; ret = winnt_scan_efsrpc_raw_data(inode, ctx); if (ret) goto out; } else { /* * Load information about data streams (unnamed and named). * * Skip this step for encrypted files, since the data from * ReadEncryptedFileRaw() already contains all data streams (and * they do in fact all get restored by WriteEncryptedFileRaw().) * * Note: WIMGAPI (as of Windows 8.1) gets wrong and stores both * the EFSRPC data and the named data stream(s)...! */ ret = winnt_scan_data_streams(h, inode, file_info.end_of_file, ctx); if (ret) goto out; } if (unlikely(should_try_to_use_wimboot_hash(inode, ctx))) { ret = try_to_use_wimboot_hash(h, inode, ctx); if (ret) goto out; } set_sort_key(inode, sort_key); if (inode_is_directory(inode)) { /* Directory: recurse to children. */ /* Re-open the directory with FILE_LIST_DIRECTORY access. */ if (h) { NtClose(h); h = NULL; } status = winnt_openat(cur_dir, relative_path, relative_path_nchars, FILE_LIST_DIRECTORY, &h); if (!NT_SUCCESS(status)) { winnt_error(status, L"\"%ls\": Can't open directory", printable_path(ctx)); ret = WIMLIB_ERR_OPEN; goto out; } ret = winnt_recurse_directory(h, root, ctx); if (ret) goto out; } out_progress: if (likely(root)) ret = do_scan_progress(ctx->params, WIMLIB_SCAN_DENTRY_OK, inode); else ret = do_scan_progress(ctx->params, WIMLIB_SCAN_DENTRY_EXCLUDED, NULL); out: if (likely(h)) NtClose(h); if (unlikely(ret)) { free_dentry_tree(root, ctx->params->blob_table); root = NULL; ret = report_scan_error(ctx->params, ret); } *root_ret = root; return ret; } static void winnt_do_scan_warnings(const wchar_t *path, const struct winnt_scan_ctx *ctx) { if (likely(ctx->num_get_sacl_priv_notheld == 0 && ctx->num_get_sd_access_denied == 0)) return; WARNING("Scan of \"%ls\" complete, but with one or more warnings:", path); if (ctx->num_get_sacl_priv_notheld != 0) { WARNING("- Could not capture SACL (System Access Control List)\n" " on %lu files or directories.", ctx->num_get_sacl_priv_notheld); } if (ctx->num_get_sd_access_denied != 0) { WARNING("- Could not capture security descriptor at all\n" " on %lu files or directories.", ctx->num_get_sd_access_denied); } WARNING("To fully capture all security descriptors, run the program\n" " with Administrator rights."); } /*----------------------------------------------------------------------------* * Fast MFT scan implementation * *----------------------------------------------------------------------------*/ #define ENABLE_FAST_MFT_SCAN 1 #ifdef ENABLE_FAST_MFT_SCAN typedef struct { u64 StartingCluster; u64 ClusterCount; } CLUSTER_RANGE; typedef struct { u64 StartingFileReferenceNumber; u64 EndingFileReferenceNumber; } FILE_REFERENCE_RANGE; /* The FSCTL_QUERY_FILE_LAYOUT ioctl. This ioctl can be used on Windows 8 and * later to scan the MFT of an NTFS volume. */ #define FSCTL_QUERY_FILE_LAYOUT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 157, METHOD_NEITHER, FILE_ANY_ACCESS) /* The input to FSCTL_QUERY_FILE_LAYOUT */ typedef struct { u32 NumberOfPairs; #define QUERY_FILE_LAYOUT_RESTART 0x00000001 #define QUERY_FILE_LAYOUT_INCLUDE_NAMES 0x00000002 #define QUERY_FILE_LAYOUT_INCLUDE_STREAMS 0x00000004 #define QUERY_FILE_LAYOUT_INCLUDE_EXTENTS 0x00000008 #define QUERY_FILE_LAYOUT_INCLUDE_EXTRA_INFO 0x00000010 #define QUERY_FILE_LAYOUT_INCLUDE_STREAMS_WITH_NO_CLUSTERS_ALLOCATED 0x00000020 u32 Flags; #define QUERY_FILE_LAYOUT_FILTER_TYPE_NONE 0 #define QUERY_FILE_LAYOUT_FILTER_TYPE_CLUSTERS 1 #define QUERY_FILE_LAYOUT_FILTER_TYPE_FILEID 2 #define QUERY_FILE_LAYOUT_NUM_FILTER_TYPES 3 u32 FilterType; u32 Reserved; union { CLUSTER_RANGE ClusterRanges[1]; FILE_REFERENCE_RANGE FileReferenceRanges[1]; } Filter; } QUERY_FILE_LAYOUT_INPUT; /* The header of the buffer returned by FSCTL_QUERY_FILE_LAYOUT */ typedef struct { u32 FileEntryCount; u32 FirstFileOffset; #define QUERY_FILE_LAYOUT_SINGLE_INSTANCED 0x00000001 u32 Flags; u32 Reserved; } QUERY_FILE_LAYOUT_OUTPUT; /* Inode information returned by FSCTL_QUERY_FILE_LAYOUT */ typedef struct { u32 Version; u32 NextFileOffset; u32 Flags; u32 FileAttributes; u64 FileReferenceNumber; u32 FirstNameOffset; u32 FirstStreamOffset; u32 ExtraInfoOffset; u32 Reserved; } FILE_LAYOUT_ENTRY; /* Extra inode information returned by FSCTL_QUERY_FILE_LAYOUT */ typedef struct { struct { u64 CreationTime; u64 LastAccessTime; u64 LastWriteTime; u64 ChangeTime; u32 FileAttributes; } BasicInformation; u32 OwnerId; u32 SecurityId; s64 Usn; } FILE_LAYOUT_INFO_ENTRY; /* Filename (or dentry) information returned by FSCTL_QUERY_FILE_LAYOUT */ typedef struct { u32 NextNameOffset; #define FILE_LAYOUT_NAME_ENTRY_PRIMARY 0x00000001 #define FILE_LAYOUT_NAME_ENTRY_DOS 0x00000002 u32 Flags; u64 ParentFileReferenceNumber; u32 FileNameLength; u32 Reserved; wchar_t FileName[1]; } FILE_LAYOUT_NAME_ENTRY; /* Stream information returned by FSCTL_QUERY_FILE_LAYOUT */ typedef struct { u32 Version; u32 NextStreamOffset; #define STREAM_LAYOUT_ENTRY_IMMOVABLE 0x00000001 #define STREAM_LAYOUT_ENTRY_PINNED 0x00000002 #define STREAM_LAYOUT_ENTRY_RESIDENT 0x00000004 #define STREAM_LAYOUT_ENTRY_NO_CLUSTERS_ALLOCATED 0x00000008 u32 Flags; u32 ExtentInformationOffset; u64 AllocationSize; u64 EndOfFile; u64 Reserved; u32 AttributeFlags; u32 StreamIdentifierLength; wchar_t StreamIdentifier[1]; } STREAM_LAYOUT_ENTRY; typedef struct { #define STREAM_EXTENT_ENTRY_AS_RETRIEVAL_POINTERS 0x00000001 #define STREAM_EXTENT_ENTRY_ALL_EXTENTS 0x00000002 u32 Flags; union { RETRIEVAL_POINTERS_BUFFER RetrievalPointers; } ExtentInformation; } STREAM_EXTENT_ENTRY; /* Extract the MFT number part of the full inode number */ #define NTFS_MFT_NO(ref) ((ref) & (((u64)1 << 48) - 1)) /* Is the file the root directory of the NTFS volume? The root directory always * occupies MFT record 5. */ #define NTFS_IS_ROOT_FILE(ino) (NTFS_MFT_NO(ino) == 5) /* Is the file a special NTFS file, other than the root directory? The special * files are the first 16 records in the MFT. */ #define NTFS_IS_SPECIAL_FILE(ino) \ (NTFS_MFT_NO(ino) <= 15 && !NTFS_IS_ROOT_FILE(ino)) #define NTFS_SPECIAL_STREAM_OBJECT_ID 0x00000001 #define NTFS_SPECIAL_STREAM_EA 0x00000002 #define NTFS_SPECIAL_STREAM_EA_INFORMATION 0x00000004 /* Intermediate inode structure. This is used to temporarily save information * from FSCTL_QUERY_FILE_LAYOUT before creating the full 'struct wim_inode'. */ struct ntfs_inode { struct avl_tree_node index_node; u64 ino; u64 creation_time; u64 last_access_time; u64 last_write_time; u64 starting_lcn; u32 attributes; u32 security_id; u32 num_aliases; u32 num_streams; u32 special_streams; u32 first_stream_offset; struct ntfs_dentry *first_child; wchar_t short_name[13]; }; /* Intermediate dentry structure. This is used to temporarily save information * from FSCTL_QUERY_FILE_LAYOUT before creating the full 'struct wim_dentry'. */ struct ntfs_dentry { u32 offset_from_inode : 31; u32 is_primary : 1; union { /* Note: build_children_lists() replaces 'parent_ino' with * 'next_child'. */ u64 parent_ino; struct ntfs_dentry *next_child; }; wchar_t name[0]; }; /* Intermediate stream structure. This is used to temporarily save information * from FSCTL_QUERY_FILE_LAYOUT before creating the full 'struct * wim_inode_stream'. */ struct ntfs_stream { u64 size; wchar_t name[0]; }; /* Map of all known NTFS inodes, keyed by inode number */ struct ntfs_inode_map { struct avl_tree_node *root; }; #define NTFS_INODE(node) \ avl_tree_entry((node), struct ntfs_inode, index_node) #define SKIP_ALIGNED(p, size) ((void *)(p) + ALIGN((size), 8)) /* Get a pointer to the first dentry of the inode. */ #define FIRST_DENTRY(ni) SKIP_ALIGNED((ni), sizeof(struct ntfs_inode)) /* Get a pointer to the first stream of the inode. */ #define FIRST_STREAM(ni) ((const void *)ni + ni->first_stream_offset) /* Advance to the next dentry of the inode. */ #define NEXT_DENTRY(nd) SKIP_ALIGNED((nd), sizeof(struct ntfs_dentry) + \ (wcslen((nd)->name) + 1) * sizeof(wchar_t)) /* Advance to the next stream of the inode. */ #define NEXT_STREAM(ns) SKIP_ALIGNED((ns), sizeof(struct ntfs_stream) + \ (wcslen((ns)->name) + 1) * sizeof(wchar_t)) static int _avl_cmp_ntfs_inodes(const struct avl_tree_node *node1, const struct avl_tree_node *node2) { return cmp_u64(NTFS_INODE(node1)->ino, NTFS_INODE(node2)->ino); } /* Adds an NTFS inode to the map. */ static void ntfs_inode_map_add_inode(struct ntfs_inode_map *map, struct ntfs_inode *ni) { if (avl_tree_insert(&map->root, &ni->index_node, _avl_cmp_ntfs_inodes)) { WARNING("Inode 0x%016"PRIx64" is a duplicate!", ni->ino); FREE(ni); } } /* Find an ntfs_inode in the map by inode number. Returns NULL if not found. */ static struct ntfs_inode * ntfs_inode_map_lookup(struct ntfs_inode_map *map, u64 ino) { struct ntfs_inode tmp; struct avl_tree_node *res; tmp.ino = ino; res = avl_tree_lookup_node(map->root, &tmp.index_node, _avl_cmp_ntfs_inodes); if (!res) return NULL; return NTFS_INODE(res); } /* Remove an ntfs_inode from the map and free it. */ static void ntfs_inode_map_remove(struct ntfs_inode_map *map, struct ntfs_inode *ni) { avl_tree_remove(&map->root, &ni->index_node); FREE(ni); } /* Free all ntfs_inodes in the map. */ static void ntfs_inode_map_destroy(struct ntfs_inode_map *map) { struct ntfs_inode *ni; avl_tree_for_each_in_postorder(ni, map->root, struct ntfs_inode, index_node) FREE(ni); } static bool file_has_streams(const FILE_LAYOUT_ENTRY *file) { return (file->FirstStreamOffset != 0) && !(file->FileAttributes & FILE_ATTRIBUTE_ENCRYPTED); } static bool is_valid_name_entry(const FILE_LAYOUT_NAME_ENTRY *name) { return name->FileNameLength > 0 && name->FileNameLength % 2 == 0 && !wmemchr(name->FileName, L'\0', name->FileNameLength / 2) && (!(name->Flags & FILE_LAYOUT_NAME_ENTRY_DOS) || name->FileNameLength <= 24); } /* Validate the FILE_LAYOUT_NAME_ENTRYs of the specified file and compute the * total length in bytes of the ntfs_dentry structures needed to hold the name * information. */ static int validate_names_and_compute_total_length(const FILE_LAYOUT_ENTRY *file, size_t *total_length_ret) { const FILE_LAYOUT_NAME_ENTRY *name = (const void *)file + file->FirstNameOffset; size_t total = 0; size_t num_long_names = 0; for (;;) { if (unlikely(!is_valid_name_entry(name))) { ERROR("Invalid FILE_LAYOUT_NAME_ENTRY! " "FileReferenceNumber=0x%016"PRIx64", " "FileNameLength=%"PRIu32", " "FileName=%.*ls, Flags=0x%08"PRIx32, file->FileReferenceNumber, name->FileNameLength, (int)(name->FileNameLength / 2), name->FileName, name->Flags); return WIMLIB_ERR_UNSUPPORTED; } if (name->Flags != FILE_LAYOUT_NAME_ENTRY_DOS) { num_long_names++; total += ALIGN(sizeof(struct ntfs_dentry) + name->FileNameLength + sizeof(wchar_t), 8); } if (name->NextNameOffset == 0) break; name = (const void *)name + name->NextNameOffset; } if (unlikely(num_long_names == 0)) { ERROR("Inode 0x%016"PRIx64" has no long names!", file->FileReferenceNumber); return WIMLIB_ERR_UNSUPPORTED; } *total_length_ret = total; return 0; } static bool is_valid_stream_entry(const STREAM_LAYOUT_ENTRY *stream) { return stream->StreamIdentifierLength % 2 == 0 && !wmemchr(stream->StreamIdentifier , L'\0', stream->StreamIdentifierLength / 2); } /* assumes that 'id' is a wide string literal */ #define stream_has_identifier(stream, id) \ ((stream)->StreamIdentifierLength == sizeof(id) - 2 && \ !memcmp((stream)->StreamIdentifier, id, sizeof(id) - 2)) /* * If the specified STREAM_LAYOUT_ENTRY represents a DATA stream as opposed to * some other type of NTFS stream such as a STANDARD_INFORMATION stream, return * true and set *stream_name_ret and *stream_name_nchars_ret to specify just the * stream name. For example, ":foo:$DATA" would become "foo" with length 3 * characters. Otherwise return false. */ static bool use_stream(const FILE_LAYOUT_ENTRY *file, const STREAM_LAYOUT_ENTRY *stream, const wchar_t **stream_name_ret, size_t *stream_name_nchars_ret) { const wchar_t *stream_name; size_t stream_name_nchars; if (stream->StreamIdentifierLength == 0) { /* The unnamed data stream may be given as an empty string * rather than as "::$DATA". Handle it both ways. */ stream_name = L""; stream_name_nchars = 0; } else if (!get_data_stream_name(stream->StreamIdentifier, stream->StreamIdentifierLength / 2, &stream_name, &stream_name_nchars)) return false; /* Skip the unnamed data stream for directories. */ if (stream_name_nchars == 0 && (file->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) return false; *stream_name_ret = stream_name; *stream_name_nchars_ret = stream_name_nchars; return true; } /* Validate the STREAM_LAYOUT_ENTRYs of the specified file and compute the total * length in bytes of the ntfs_stream structures needed to hold the stream * information. In addition, set *special_streams_ret to a bitmask of special * stream types that were found. */ static int validate_streams_and_compute_total_length(const FILE_LAYOUT_ENTRY *file, size_t *total_length_ret, u32 *special_streams_ret) { const STREAM_LAYOUT_ENTRY *stream = (const void *)file + file->FirstStreamOffset; size_t total = 0; u32 special_streams = 0; for (;;) { const wchar_t *name; size_t name_nchars; if (unlikely(!is_valid_stream_entry(stream))) { WARNING("Invalid STREAM_LAYOUT_ENTRY! " "FileReferenceNumber=0x%016"PRIx64", " "StreamIdentifierLength=%"PRIu32", " "StreamIdentifier=%.*ls", file->FileReferenceNumber, stream->StreamIdentifierLength, (int)(stream->StreamIdentifierLength / 2), stream->StreamIdentifier); return WIMLIB_ERR_UNSUPPORTED; } if (use_stream(file, stream, &name, &name_nchars)) { total += ALIGN(sizeof(struct ntfs_stream) + (name_nchars + 1) * sizeof(wchar_t), 8); } else if (stream_has_identifier(stream, L"::$OBJECT_ID")) { special_streams |= NTFS_SPECIAL_STREAM_OBJECT_ID; } else if (stream_has_identifier(stream, L"::$EA")) { special_streams |= NTFS_SPECIAL_STREAM_EA; } else if (stream_has_identifier(stream, L"::$EA_INFORMATION")) { special_streams |= NTFS_SPECIAL_STREAM_EA_INFORMATION; } if (stream->NextStreamOffset == 0) break; stream = (const void *)stream + stream->NextStreamOffset; } *total_length_ret = total; *special_streams_ret = special_streams; return 0; } static void * load_name_information(const FILE_LAYOUT_ENTRY *file, struct ntfs_inode *ni, void *p) { const FILE_LAYOUT_NAME_ENTRY *name = (const void *)file + file->FirstNameOffset; for (;;) { struct ntfs_dentry *nd = p; /* Note that a name may be just a short (DOS) name, just a long * name, or both a short name and a long name. If there is a * short name, one name should also be marked as "primary" to * indicate which long name the short name is associated with. * Also, there should be at most one short name per inode. */ if (name->Flags & FILE_LAYOUT_NAME_ENTRY_DOS) { memcpy(ni->short_name, name->FileName, name->FileNameLength); ni->short_name[name->FileNameLength / 2] = L'\0'; } if (name->Flags != FILE_LAYOUT_NAME_ENTRY_DOS) { ni->num_aliases++; nd->offset_from_inode = (u8 *)nd - (u8 *)ni; nd->is_primary = ((name->Flags & FILE_LAYOUT_NAME_ENTRY_PRIMARY) != 0); nd->parent_ino = name->ParentFileReferenceNumber; memcpy(nd->name, name->FileName, name->FileNameLength); nd->name[name->FileNameLength / 2] = L'\0'; p += ALIGN(sizeof(struct ntfs_dentry) + name->FileNameLength + sizeof(wchar_t), 8); } if (name->NextNameOffset == 0) break; name = (const void *)name + name->NextNameOffset; } return p; } static u64 load_starting_lcn(const STREAM_LAYOUT_ENTRY *stream) { const STREAM_EXTENT_ENTRY *entry; if (stream->ExtentInformationOffset == 0) return 0; entry = (const void *)stream + stream->ExtentInformationOffset; if (!(entry->Flags & STREAM_EXTENT_ENTRY_AS_RETRIEVAL_POINTERS)) return 0; return extract_starting_lcn(&entry->ExtentInformation.RetrievalPointers); } static void * load_stream_information(const FILE_LAYOUT_ENTRY *file, struct ntfs_inode *ni, void *p) { const STREAM_LAYOUT_ENTRY *stream = (const void *)file + file->FirstStreamOffset; const u32 first_stream_offset = (const u8 *)p - (const u8 *)ni; for (;;) { struct ntfs_stream *ns = p; const wchar_t *name; size_t name_nchars; if (use_stream(file, stream, &name, &name_nchars)) { ni->first_stream_offset = first_stream_offset; ni->num_streams++; if (name_nchars == 0) ni->starting_lcn = load_starting_lcn(stream); ns->size = stream->EndOfFile; wmemcpy(ns->name, name, name_nchars); ns->name[name_nchars] = L'\0'; p += ALIGN(sizeof(struct ntfs_stream) + (name_nchars + 1) * sizeof(wchar_t), 8); } if (stream->NextStreamOffset == 0) break; stream = (const void *)stream + stream->NextStreamOffset; } return p; } /* Process the information for a file given by FSCTL_QUERY_FILE_LAYOUT. */ static int load_one_file(const FILE_LAYOUT_ENTRY *file, struct ntfs_inode_map *inode_map) { const FILE_LAYOUT_INFO_ENTRY *info = (const void *)file + file->ExtraInfoOffset; size_t inode_size; struct ntfs_inode *ni; size_t n; int ret; void *p; u32 special_streams = 0; inode_size = ALIGN(sizeof(struct ntfs_inode), 8); /* The root file should have no names, and all other files should have * at least one name. But just in case, we ignore the names of the root * file, and we ignore any non-root file with no names. */ if (!NTFS_IS_ROOT_FILE(file->FileReferenceNumber)) { if (file->FirstNameOffset == 0) return 0; ret = validate_names_and_compute_total_length(file, &n); if (ret) return ret; inode_size += n; } if (file_has_streams(file)) { ret = validate_streams_and_compute_total_length(file, &n, &special_streams); if (ret) return ret; inode_size += n; } /* To save memory, we allocate the ntfs_dentry's and ntfs_stream's in * the same memory block as their ntfs_inode. */ ni = CALLOC(1, inode_size); if (!ni) return WIMLIB_ERR_NOMEM; ni->ino = file->FileReferenceNumber; ni->attributes = info->BasicInformation.FileAttributes; ni->creation_time = info->BasicInformation.CreationTime; ni->last_write_time = info->BasicInformation.LastWriteTime; ni->last_access_time = info->BasicInformation.LastAccessTime; ni->security_id = info->SecurityId; ni->special_streams = special_streams; p = FIRST_DENTRY(ni); if (!NTFS_IS_ROOT_FILE(file->FileReferenceNumber)) p = load_name_information(file, ni, p); if (file_has_streams(file)) p = load_stream_information(file, ni, p); wimlib_assert((u8 *)p - (u8 *)ni == inode_size); ntfs_inode_map_add_inode(inode_map, ni); return 0; } /* * Quickly find all files on an NTFS volume by using FSCTL_QUERY_FILE_LAYOUT to * scan the MFT. The NTFS volume is specified by the NT namespace path @path. * For each file, allocate an 'ntfs_inode' structure for each file and add it to * 'inode_map' keyed by inode number. Include NTFS special files such as * $Bitmap (they will be removed later). */ static int load_files_from_mft(const wchar_t *path, struct ntfs_inode_map *inode_map) { HANDLE h = NULL; QUERY_FILE_LAYOUT_INPUT in = (QUERY_FILE_LAYOUT_INPUT) { .NumberOfPairs = 0, .Flags = QUERY_FILE_LAYOUT_RESTART | QUERY_FILE_LAYOUT_INCLUDE_NAMES | QUERY_FILE_LAYOUT_INCLUDE_STREAMS | QUERY_FILE_LAYOUT_INCLUDE_EXTENTS | QUERY_FILE_LAYOUT_INCLUDE_EXTRA_INFO | QUERY_FILE_LAYOUT_INCLUDE_STREAMS_WITH_NO_CLUSTERS_ALLOCATED, .FilterType = QUERY_FILE_LAYOUT_FILTER_TYPE_NONE, }; size_t outsize = 32768; QUERY_FILE_LAYOUT_OUTPUT *out = NULL; int ret; NTSTATUS status; status = winnt_open(path, wcslen(path), FILE_READ_DATA | FILE_READ_ATTRIBUTES, &h); if (!NT_SUCCESS(status)) { ret = -1; /* Silently try standard recursive scan instead */ goto out; } for (;;) { /* Allocate a buffer for the output of the ioctl. */ out = MALLOC(outsize); if (!out) { ret = WIMLIB_ERR_NOMEM; goto out; } /* Execute FSCTL_QUERY_FILE_LAYOUT until it fails. */ while (NT_SUCCESS(status = winnt_fsctl(h, FSCTL_QUERY_FILE_LAYOUT, &in, sizeof(in), out, outsize, NULL))) { const FILE_LAYOUT_ENTRY *file = (const void *)out + out->FirstFileOffset; for (;;) { ret = load_one_file(file, inode_map); if (ret) goto out; if (file->NextFileOffset == 0) break; file = (const void *)file + file->NextFileOffset; } in.Flags &= ~QUERY_FILE_LAYOUT_RESTART; } /* Enlarge the buffer if needed. */ if (status != STATUS_BUFFER_TOO_SMALL) break; FREE(out); outsize *= 2; } /* Normally, FSCTL_QUERY_FILE_LAYOUT fails with STATUS_END_OF_FILE after * all files have been enumerated. */ if (status != STATUS_END_OF_FILE) { if (status == STATUS_INVALID_DEVICE_REQUEST /* old OS */ || status == STATUS_NOT_SUPPORTED /* Samba volume, WinXP */ || status == STATUS_INVALID_PARAMETER /* not root directory */ ) { /* Silently try standard recursive scan instead */ ret = -1; } else { winnt_error(status, L"Error enumerating files on volume \"%ls\"", path); /* Try standard recursive scan instead */ ret = WIMLIB_ERR_UNSUPPORTED; } goto out; } ret = 0; out: FREE(out); NtClose(h); return ret; } /* Build the list of child dentries for each inode in @map. This is done by * iterating through each name of each inode and adding it to its parent's * children list. Note that every name should have a parent, i.e. should belong * to some directory. The root directory does not have any names. */ static int build_children_lists(struct ntfs_inode_map *map, struct ntfs_inode **root_ret) { struct ntfs_inode *ni; avl_tree_for_each_in_order(ni, map->root, struct ntfs_inode, index_node) { struct ntfs_dentry *nd; u32 n; if (NTFS_IS_ROOT_FILE(ni->ino)) { *root_ret = ni; continue; } n = ni->num_aliases; nd = FIRST_DENTRY(ni); for (;;) { struct ntfs_inode *parent; parent = ntfs_inode_map_lookup(map, nd->parent_ino); if (unlikely(!parent)) { ERROR("Parent inode 0x%016"PRIx64" of" "directory entry \"%ls\" (inode " "0x%016"PRIx64") was missing from the " "MFT listing!", nd->parent_ino, nd->name, ni->ino); return WIMLIB_ERR_UNSUPPORTED; } nd->next_child = parent->first_child; parent->first_child = nd; if (!--n) break; nd = NEXT_DENTRY(nd); } } return 0; } struct security_map_node { struct avl_tree_node index_node; u32 disk_security_id; u32 wim_security_id; }; /* Map from disk security IDs to WIM security IDs */ struct security_map { struct avl_tree_node *root; }; #define SECURITY_MAP_NODE(node) \ avl_tree_entry((node), struct security_map_node, index_node) static int _avl_cmp_security_map_nodes(const struct avl_tree_node *node1, const struct avl_tree_node *node2) { return cmp_u32(SECURITY_MAP_NODE(node1)->disk_security_id, SECURITY_MAP_NODE(node2)->disk_security_id); } static s32 security_map_lookup(struct security_map *map, u32 disk_security_id) { struct security_map_node tmp; const struct avl_tree_node *res; if (disk_security_id == 0) /* No on-disk security ID; uncacheable */ return -1; tmp.disk_security_id = disk_security_id; res = avl_tree_lookup_node(map->root, &tmp.index_node, _avl_cmp_security_map_nodes); if (!res) return -1; return SECURITY_MAP_NODE(res)->wim_security_id; } static int security_map_insert(struct security_map *map, u32 disk_security_id, u32 wim_security_id) { struct security_map_node *node; if (disk_security_id == 0) /* No on-disk security ID; uncacheable */ return 0; node = MALLOC(sizeof(*node)); if (!node) return WIMLIB_ERR_NOMEM; node->disk_security_id = disk_security_id; node->wim_security_id = wim_security_id; avl_tree_insert(&map->root, &node->index_node, _avl_cmp_security_map_nodes); return 0; } static void security_map_destroy(struct security_map *map) { struct security_map_node *node; avl_tree_for_each_in_postorder(node, map->root, struct security_map_node, index_node) FREE(node); } /* * Turn our temporary NTFS structures into the final WIM structures: * * ntfs_inode => wim_inode * ntfs_dentry => wim_dentry * ntfs_stream => wim_inode_stream * * This also handles things such as exclusions and issuing progress messages. * It's similar to winnt_build_dentry_tree_recursive(), but this is much faster * because almost all information we need is already loaded in memory in the * ntfs_* structures. However, in some cases we still fall back to * winnt_build_dentry_tree_recursive() and/or opening the file. */ static int generate_wim_structures_recursive(struct wim_dentry **root_ret, const wchar_t *filename, bool is_primary_name, struct ntfs_inode *ni, struct winnt_scan_ctx *ctx, struct ntfs_inode_map *inode_map, struct security_map *security_map) { int ret = 0; struct wim_dentry *root = NULL; struct wim_inode *inode = NULL; const struct ntfs_stream *ns; /* Completely ignore NTFS special files. */ if (NTFS_IS_SPECIAL_FILE(ni->ino)) goto out; /* Fall back to a recursive scan for unhandled cases. Reparse points, * in particular, can't be properly handled here because a commonly used * filter driver (WOF) hides reparse points from regular filesystem APIs * but not from FSCTL_QUERY_FILE_LAYOUT. */ if (ni->attributes & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_ENCRYPTED) || ni->special_streams != 0) { ret = winnt_build_dentry_tree_recursive(&root, NULL, ctx->params->cur_path, ctx->params->cur_path_nchars, filename, ctx); goto out; } /* Test for exclusion based on path. */ ret = try_exclude(ctx->params); if (unlikely(ret < 0)) /* Excluded? */ goto out_progress; if (unlikely(ret > 0)) /* Error? */ goto out; /* Create the WIM dentry and possibly a new WIM inode */ ret = inode_table_new_dentry(ctx->params->inode_table, filename, ni->ino, ctx->params->capture_root_dev, false, &root); if (ret) goto out; inode = root->d_inode; /* Set the short name if needed. */ if (is_primary_name && *ni->short_name) { size_t nbytes = wcslen(ni->short_name) * sizeof(wchar_t); root->d_short_name = memdup(ni->short_name, nbytes + sizeof(wchar_t)); if (!root->d_short_name) { ret = WIMLIB_ERR_NOMEM; goto out; } root->d_short_name_nbytes = nbytes; } if (inode->i_nlink > 1) { /* Already seen this inode? */ ret = 0; goto out_progress; } /* The file attributes and timestamps were cached from the MFT. */ inode->i_attributes = ni->attributes; inode->i_creation_time = ni->creation_time; inode->i_last_write_time = ni->last_write_time; inode->i_last_access_time = ni->last_access_time; /* Set the security descriptor if needed. */ if (!(ctx->params->add_flags & WIMLIB_ADD_FLAG_NO_ACLS)) { /* Look up the WIM security ID that corresponds to the on-disk * security ID. */ s32 wim_security_id = security_map_lookup(security_map, ni->security_id); if (likely(wim_security_id >= 0)) { /* The mapping for this security ID is already cached.*/ inode->i_security_id = wim_security_id; } else { HANDLE h; NTSTATUS status; /* Create a mapping for this security ID and insert it * into the security map. */ status = winnt_open(ctx->params->cur_path, ctx->params->cur_path_nchars, READ_CONTROL | ACCESS_SYSTEM_SECURITY, &h); if (!NT_SUCCESS(status)) { winnt_error(status, L"Can't open \"%ls\" to " "read security descriptor", printable_path(ctx)); ret = WIMLIB_ERR_OPEN; goto out; } ret = winnt_load_security_descriptor(h, inode, ctx); NtClose(h); if (ret) goto out; ret = security_map_insert(security_map, ni->security_id, inode->i_security_id); if (ret) goto out; } } /* Add data streams based on the cached information from the MFT. */ ns = FIRST_STREAM(ni); for (u32 i = 0; i < ni->num_streams; i++) { struct windows_file *windows_file; /* Reference the stream by path if it's a named data stream, or * if the volume doesn't support "open by file ID", or if the * application hasn't explicitly opted in to "open by file ID". * Otherwise, only save the inode number (file ID). */ if (*ns->name || !(ctx->vol_flags & FILE_SUPPORTS_OPEN_BY_FILE_ID) || !(ctx->params->add_flags & WIMLIB_ADD_FLAG_FILE_PATHS_UNNEEDED)) { windows_file = alloc_windows_file(ctx->params->cur_path, ctx->params->cur_path_nchars, ns->name, wcslen(ns->name), ctx->snapshot, false); } else { windows_file = alloc_windows_file_for_file_id(ni->ino, ctx->params->cur_path, ctx->params->root_path_nchars, ctx->snapshot); } ret = add_stream(inode, windows_file, ns->size, STREAM_TYPE_DATA, ns->name, ctx->params->unhashed_blobs); if (ret) goto out; ns = NEXT_STREAM(ns); } set_sort_key(inode, ni->starting_lcn); /* If processing a directory, then recurse to its children. In this * version there is no need to go to disk, as we already have the list * of children cached from the MFT. */ if (inode_is_directory(inode)) { const struct ntfs_dentry *nd = ni->first_child; while (nd != NULL) { size_t orig_path_nchars; struct wim_dentry *child; const struct ntfs_dentry *next = nd->next_child; ret = WIMLIB_ERR_NOMEM; if (!pathbuf_append_name(ctx->params, nd->name, wcslen(nd->name), &orig_path_nchars)) goto out; ret = generate_wim_structures_recursive( &child, nd->name, nd->is_primary, (void *)nd - nd->offset_from_inode, ctx, inode_map, security_map); pathbuf_truncate(ctx->params, orig_path_nchars); if (ret) goto out; attach_scanned_tree(root, child, ctx->params->blob_table); nd = next; } } out_progress: if (likely(root)) ret = do_scan_progress(ctx->params, WIMLIB_SCAN_DENTRY_OK, inode); else ret = do_scan_progress(ctx->params, WIMLIB_SCAN_DENTRY_EXCLUDED, NULL); out: if (--ni->num_aliases == 0) { /* Memory usage optimization: when we don't need the ntfs_inode * (and its names and streams) anymore, free it. */ ntfs_inode_map_remove(inode_map, ni); } if (unlikely(ret)) { free_dentry_tree(root, ctx->params->blob_table); root = NULL; } *root_ret = root; return ret; } static int winnt_build_dentry_tree_fast(struct wim_dentry **root_ret, struct winnt_scan_ctx *ctx) { struct ntfs_inode_map inode_map = { .root = NULL }; struct security_map security_map = { .root = NULL }; struct ntfs_inode *root = NULL; wchar_t *path = ctx->params->cur_path; size_t path_nchars = ctx->params->cur_path_nchars; bool adjust_path; int ret; adjust_path = (path[path_nchars - 1] == L'\\'); if (adjust_path) path[path_nchars - 1] = L'\0'; ret = load_files_from_mft(path, &inode_map); if (adjust_path) path[path_nchars - 1] = L'\\'; if (ret) goto out; ret = build_children_lists(&inode_map, &root); if (ret) goto out; if (!root) { ERROR("The MFT listing for volume \"%ls\" did not include a " "root directory!", path); ret = WIMLIB_ERR_UNSUPPORTED; goto out; } root->num_aliases = 1; ret = generate_wim_structures_recursive(root_ret, L"", false, root, ctx, &inode_map, &security_map); out: ntfs_inode_map_destroy(&inode_map); security_map_destroy(&security_map); return ret; } #endif /* ENABLE_FAST_MFT_SCAN */ /*----------------------------------------------------------------------------* * Entry point for directory tree scans on Windows * *----------------------------------------------------------------------------*/ int win32_build_dentry_tree(struct wim_dentry **root_ret, const wchar_t *root_disk_path, struct scan_params *params) { struct winnt_scan_ctx ctx = { .params = params }; UNICODE_STRING ntpath; HANDLE h = NULL; NTSTATUS status; int ret; if (params->add_flags & WIMLIB_ADD_FLAG_SNAPSHOT) ret = vss_create_snapshot(root_disk_path, &ntpath, &ctx.snapshot); else ret = win32_path_to_nt_path(root_disk_path, &ntpath); if (ret) goto out; if (ntpath.Length < 4 * sizeof(wchar_t) || wmemcmp(ntpath.Buffer, L"\\??\\", 4)) { ERROR("\"%ls\": unrecognized path format", root_disk_path); ret = WIMLIB_ERR_INVALID_PARAM; } else { ret = pathbuf_init(params, ntpath.Buffer); } HeapFree(GetProcessHeap(), 0, ntpath.Buffer); if (ret) goto out; status = winnt_open(params->cur_path, params->cur_path_nchars, FILE_READ_ATTRIBUTES, &h); if (!NT_SUCCESS(status)) { winnt_error(status, L"Can't open \"%ls\"", root_disk_path); if (status == STATUS_FVE_LOCKED_VOLUME) ret = WIMLIB_ERR_FVE_LOCKED_VOLUME; else ret = WIMLIB_ERR_OPEN; goto out; } get_volume_information(h, &ctx); NtClose(h); #ifdef ENABLE_FAST_MFT_SCAN if (ctx.is_ntfs && !_wgetenv(L"WIMLIB_DISABLE_QUERY_FILE_LAYOUT")) { ret = winnt_build_dentry_tree_fast(root_ret, &ctx); if (ret >= 0 && ret != WIMLIB_ERR_UNSUPPORTED) goto out; if (ret >= 0) { WARNING("A problem occurred during the fast MFT scan.\n" " Falling back to the standard " "recursive directory tree scan."); } } #endif ret = winnt_build_dentry_tree_recursive(root_ret, NULL, params->cur_path, params->cur_path_nchars, L"", &ctx); out: vss_put_snapshot(ctx.snapshot); if (ret == 0) winnt_do_scan_warnings(root_disk_path, &ctx); return ret; } #endif /* __WIN32__ */ wimlib-1.13.1/src/write.c0000644000175000017500000032056213454536733012113 00000000000000/* * write.c * * Support for writing WIM files; write a WIM file, overwrite a WIM file, write * compressed file resources, etc. */ /* * Copyright (C) 2012-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #if defined(HAVE_SYS_FILE_H) && defined(HAVE_FLOCK) /* On BSD, this should be included before "wimlib/list.h" so that "wimlib/list.h" can * override the LIST_HEAD macro. */ # include #endif #include #include #include #include #include "wimlib/alloca.h" #include "wimlib/assert.h" #include "wimlib/blob_table.h" #include "wimlib/chunk_compressor.h" #include "wimlib/endianness.h" #include "wimlib/error.h" #include "wimlib/file_io.h" #include "wimlib/header.h" #include "wimlib/inode.h" #include "wimlib/integrity.h" #include "wimlib/metadata.h" #include "wimlib/paths.h" #include "wimlib/progress.h" #include "wimlib/resource.h" #include "wimlib/solid.h" #include "wimlib/win32.h" /* win32_rename_replacement() */ #include "wimlib/write.h" #include "wimlib/xml.h" /* wimlib internal flags used when writing resources. */ #define WRITE_RESOURCE_FLAG_RECOMPRESS 0x00000001 #define WRITE_RESOURCE_FLAG_PIPABLE 0x00000002 #define WRITE_RESOURCE_FLAG_SOLID 0x00000004 #define WRITE_RESOURCE_FLAG_SEND_DONE_WITH_FILE 0x00000008 #define WRITE_RESOURCE_FLAG_SOLID_SORT 0x00000010 static int write_flags_to_resource_flags(int write_flags) { int write_resource_flags = 0; if (write_flags & WIMLIB_WRITE_FLAG_RECOMPRESS) write_resource_flags |= WRITE_RESOURCE_FLAG_RECOMPRESS; if (write_flags & WIMLIB_WRITE_FLAG_PIPABLE) write_resource_flags |= WRITE_RESOURCE_FLAG_PIPABLE; if (write_flags & WIMLIB_WRITE_FLAG_SOLID) write_resource_flags |= WRITE_RESOURCE_FLAG_SOLID; if (write_flags & WIMLIB_WRITE_FLAG_SEND_DONE_WITH_FILE_MESSAGES) write_resource_flags |= WRITE_RESOURCE_FLAG_SEND_DONE_WITH_FILE; if ((write_flags & (WIMLIB_WRITE_FLAG_SOLID | WIMLIB_WRITE_FLAG_NO_SOLID_SORT)) == WIMLIB_WRITE_FLAG_SOLID) write_resource_flags |= WRITE_RESOURCE_FLAG_SOLID_SORT; return write_resource_flags; } struct filter_context { int write_flags; WIMStruct *wim; }; /* * Determine whether the specified blob should be filtered out from the write. * * Return values: * * < 0 : The blob should be hard-filtered; that is, not included in the output * WIM file at all. * 0 : The blob should not be filtered out. * > 0 : The blob should be soft-filtered; that is, it already exists in the * WIM file and may not need to be written again. */ static int blob_filtered(const struct blob_descriptor *blob, const struct filter_context *ctx) { int write_flags; WIMStruct *wim; if (ctx == NULL) return 0; write_flags = ctx->write_flags; wim = ctx->wim; if (write_flags & WIMLIB_WRITE_FLAG_APPEND && blob->blob_location == BLOB_IN_WIM && blob->rdesc->wim == wim) return 1; if (write_flags & WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS && blob->blob_location == BLOB_IN_WIM && blob->rdesc->wim != wim) return -1; return 0; } static bool blob_hard_filtered(const struct blob_descriptor *blob, struct filter_context *ctx) { return blob_filtered(blob, ctx) < 0; } static inline bool may_soft_filter_blobs(const struct filter_context *ctx) { return ctx && (ctx->write_flags & WIMLIB_WRITE_FLAG_APPEND); } static inline bool may_hard_filter_blobs(const struct filter_context *ctx) { return ctx && (ctx->write_flags & WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS); } static inline bool may_filter_blobs(const struct filter_context *ctx) { return (may_soft_filter_blobs(ctx) || may_hard_filter_blobs(ctx)); } /* Return true if the specified blob is located in a WIM resource which can be * reused in the output WIM file, without being recompressed. */ static bool can_raw_copy(const struct blob_descriptor *blob, int write_resource_flags, int out_ctype, u32 out_chunk_size) { const struct wim_resource_descriptor *rdesc; /* Recompress everything if requested. */ if (write_resource_flags & WRITE_RESOURCE_FLAG_RECOMPRESS) return false; /* A blob not located in a WIM resource cannot be reused. */ if (blob->blob_location != BLOB_IN_WIM) return false; rdesc = blob->rdesc; /* In the case of an in-place compaction, always reuse resources located * in the WIM being compacted. */ if (rdesc->wim->being_compacted) return true; /* Otherwise, only reuse compressed resources. */ if (out_ctype == WIMLIB_COMPRESSION_TYPE_NONE || !(rdesc->flags & (WIM_RESHDR_FLAG_COMPRESSED | WIM_RESHDR_FLAG_SOLID))) return false; /* When writing a pipable WIM, we can only reuse pipable resources; and * when writing a non-pipable WIM, we can only reuse non-pipable * resources. */ if (rdesc->is_pipable != !!(write_resource_flags & WRITE_RESOURCE_FLAG_PIPABLE)) return false; /* When writing a solid WIM, we can only reuse solid resources; and when * writing a non-solid WIM, we can only reuse non-solid resources. */ if (!!(rdesc->flags & WIM_RESHDR_FLAG_SOLID) != !!(write_resource_flags & WRITE_RESOURCE_FLAG_SOLID)) return false; /* Note: it is theoretically possible to copy chunks of compressed data * between non-solid, solid, and pipable resources. However, we don't * currently implement this optimization because it would be complex and * would usually go unused. */ if (rdesc->flags & WIM_RESHDR_FLAG_COMPRESSED) { /* To re-use a non-solid resource, it must use the desired * compression type and chunk size. */ return (rdesc->compression_type == out_ctype && rdesc->chunk_size == out_chunk_size); } else { /* Solid resource: Such resources may contain multiple blobs, * and in general only a subset of them need to be written. As * a heuristic, re-use the raw data if more than two-thirds the * uncompressed size is being written. */ /* Note: solid resources contain a header that specifies the * compression type and chunk size; therefore we don't need to * check if they are compatible with @out_ctype and * @out_chunk_size. */ /* Did we already decide to reuse the resource? */ if (rdesc->raw_copy_ok) return true; struct blob_descriptor *res_blob; u64 write_size = 0; list_for_each_entry(res_blob, &rdesc->blob_list, rdesc_node) if (res_blob->will_be_in_output_wim) write_size += res_blob->size; return (write_size > rdesc->uncompressed_size * 2 / 3); } } static u32 reshdr_flags_for_blob(const struct blob_descriptor *blob) { u32 reshdr_flags = 0; if (blob->is_metadata) reshdr_flags |= WIM_RESHDR_FLAG_METADATA; return reshdr_flags; } static void blob_set_out_reshdr_for_reuse(struct blob_descriptor *blob) { const struct wim_resource_descriptor *rdesc; wimlib_assert(blob->blob_location == BLOB_IN_WIM); rdesc = blob->rdesc; if (rdesc->flags & WIM_RESHDR_FLAG_SOLID) { blob->out_reshdr.offset_in_wim = blob->offset_in_res; blob->out_reshdr.uncompressed_size = 0; blob->out_reshdr.size_in_wim = blob->size; blob->out_res_offset_in_wim = rdesc->offset_in_wim; blob->out_res_size_in_wim = rdesc->size_in_wim; blob->out_res_uncompressed_size = rdesc->uncompressed_size; } else { blob->out_reshdr.offset_in_wim = rdesc->offset_in_wim; blob->out_reshdr.uncompressed_size = rdesc->uncompressed_size; blob->out_reshdr.size_in_wim = rdesc->size_in_wim; } blob->out_reshdr.flags = rdesc->flags; } /* Write the header for a blob in a pipable WIM. */ static int write_pwm_blob_header(const struct blob_descriptor *blob, struct filedes *out_fd, bool compressed) { struct pwm_blob_hdr blob_hdr; u32 reshdr_flags; int ret; wimlib_assert(!blob->unhashed); blob_hdr.magic = cpu_to_le64(PWM_BLOB_MAGIC); blob_hdr.uncompressed_size = cpu_to_le64(blob->size); copy_hash(blob_hdr.hash, blob->hash); reshdr_flags = reshdr_flags_for_blob(blob); if (compressed) reshdr_flags |= WIM_RESHDR_FLAG_COMPRESSED; blob_hdr.flags = cpu_to_le32(reshdr_flags); ret = full_write(out_fd, &blob_hdr, sizeof(blob_hdr)); if (ret) ERROR_WITH_ERRNO("Error writing blob header to WIM file"); return ret; } struct write_blobs_progress_data { wimlib_progress_func_t progfunc; void *progctx; union wimlib_progress_info progress; u64 next_progress; }; static int do_write_blobs_progress(struct write_blobs_progress_data *progress_data, u64 complete_size, u32 complete_count, bool discarded) { union wimlib_progress_info *progress = &progress_data->progress; int ret; if (discarded) { progress->write_streams.total_bytes -= complete_size; progress->write_streams.total_streams -= complete_count; if (progress_data->next_progress != ~(u64)0 && progress_data->next_progress > progress->write_streams.total_bytes) { progress_data->next_progress = progress->write_streams.total_bytes; } } else { progress->write_streams.completed_bytes += complete_size; progress->write_streams.completed_streams += complete_count; } if (progress->write_streams.completed_bytes >= progress_data->next_progress) { ret = call_progress(progress_data->progfunc, WIMLIB_PROGRESS_MSG_WRITE_STREAMS, progress, progress_data->progctx); if (ret) return ret; set_next_progress(progress->write_streams.completed_bytes, progress->write_streams.total_bytes, &progress_data->next_progress); } return 0; } struct write_blobs_ctx { /* File descriptor to which the blobs are being written. */ struct filedes *out_fd; /* Blob table for the WIMStruct on whose behalf the blobs are being * written. */ struct blob_table *blob_table; /* Compression format to use. */ int out_ctype; /* Maximum uncompressed chunk size in compressed resources to use. */ u32 out_chunk_size; /* Flags that affect how the blobs will be written. */ int write_resource_flags; /* Data used for issuing WRITE_STREAMS progress. */ struct write_blobs_progress_data progress_data; struct filter_context *filter_ctx; /* Pointer to the chunk_compressor implementation being used for * compressing chunks of data, or NULL if chunks are being written * uncompressed. */ struct chunk_compressor *compressor; /* A buffer of size @out_chunk_size that has been loaned out from the * chunk compressor and is currently being filled with the uncompressed * data of the next chunk. */ u8 *cur_chunk_buf; /* Number of bytes in @cur_chunk_buf that are currently filled. */ size_t cur_chunk_buf_filled; /* List of blobs that currently have chunks being compressed. */ struct list_head blobs_being_compressed; /* List of blobs in the solid resource. Blobs are moved here after * @blobs_being_compressed only when writing a solid resource. */ struct list_head blobs_in_solid_resource; /* Current uncompressed offset in the blob being written. */ u64 cur_write_blob_offset; /* Uncompressed size of resource currently being written. */ u64 cur_write_res_size; /* Array that is filled in with compressed chunk sizes as a resource is * being written. */ u64 *chunk_csizes; /* Index of next entry in @chunk_csizes to fill in. */ size_t chunk_index; /* Number of entries in @chunk_csizes currently allocated. */ size_t num_alloc_chunks; /* Offset in the output file of the start of the chunks of the resource * currently being written. */ u64 chunks_start_offset; }; /* Reserve space for the chunk table and prepare to accumulate the chunk table * in memory. */ static int begin_chunk_table(struct write_blobs_ctx *ctx, u64 res_expected_size) { u64 expected_num_chunks; u64 expected_num_chunk_entries; size_t reserve_size; int ret; /* Calculate the number of chunks and chunk entries that should be * needed for the resource. These normally will be the final values, * but in SOLID mode some of the blobs we're planning to write into the * resource may be duplicates, and therefore discarded, potentially * decreasing the number of chunk entries needed. */ expected_num_chunks = DIV_ROUND_UP(res_expected_size, ctx->out_chunk_size); expected_num_chunk_entries = expected_num_chunks; if (!(ctx->write_resource_flags & WRITE_RESOURCE_FLAG_SOLID)) expected_num_chunk_entries--; /* Make sure the chunk_csizes array is long enough to store the * compressed size of each chunk. */ if (expected_num_chunks > ctx->num_alloc_chunks) { u64 new_length = expected_num_chunks + 50; if ((size_t)new_length != new_length) { ERROR("Resource size too large (%"PRIu64" bytes!", res_expected_size); return WIMLIB_ERR_NOMEM; } FREE(ctx->chunk_csizes); ctx->chunk_csizes = MALLOC(new_length * sizeof(ctx->chunk_csizes[0])); if (ctx->chunk_csizes == NULL) { ctx->num_alloc_chunks = 0; return WIMLIB_ERR_NOMEM; } ctx->num_alloc_chunks = new_length; } ctx->chunk_index = 0; if (!(ctx->write_resource_flags & WRITE_RESOURCE_FLAG_PIPABLE)) { /* Reserve space for the chunk table in the output file. In the * case of solid resources this reserves the upper bound for the * needed space, not necessarily the exact space which will * prove to be needed. At this point, we just use @chunk_csizes * for a buffer of 0's because the actual compressed chunk sizes * are unknown. */ reserve_size = expected_num_chunk_entries * get_chunk_entry_size(res_expected_size, 0 != (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_SOLID)); if (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_SOLID) reserve_size += sizeof(struct alt_chunk_table_header_disk); memset(ctx->chunk_csizes, 0, reserve_size); ret = full_write(ctx->out_fd, ctx->chunk_csizes, reserve_size); if (ret) { ERROR_WITH_ERRNO("Error reserving space for chunk " "table in WIM file"); return ret; } } return 0; } static int begin_write_resource(struct write_blobs_ctx *ctx, u64 res_expected_size) { int ret; wimlib_assert(res_expected_size != 0); if (ctx->compressor != NULL) { ret = begin_chunk_table(ctx, res_expected_size); if (ret) return ret; } /* Output file descriptor is now positioned at the offset at which to * write the first chunk of the resource. */ ctx->chunks_start_offset = ctx->out_fd->offset; ctx->cur_write_blob_offset = 0; ctx->cur_write_res_size = res_expected_size; return 0; } static int end_chunk_table(struct write_blobs_ctx *ctx, u64 res_actual_size, u64 *res_start_offset_ret, u64 *res_store_size_ret) { size_t actual_num_chunks; size_t actual_num_chunk_entries; size_t chunk_entry_size; int ret; actual_num_chunks = ctx->chunk_index; actual_num_chunk_entries = actual_num_chunks; if (!(ctx->write_resource_flags & WRITE_RESOURCE_FLAG_SOLID)) actual_num_chunk_entries--; chunk_entry_size = get_chunk_entry_size(res_actual_size, 0 != (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_SOLID)); typedef le64 _may_alias_attribute aliased_le64_t; typedef le32 _may_alias_attribute aliased_le32_t; if (chunk_entry_size == 4) { aliased_le32_t *entries = (aliased_le32_t*)ctx->chunk_csizes; if (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_SOLID) { for (size_t i = 0; i < actual_num_chunk_entries; i++) entries[i] = cpu_to_le32(ctx->chunk_csizes[i]); } else { u32 offset = ctx->chunk_csizes[0]; for (size_t i = 0; i < actual_num_chunk_entries; i++) { u32 next_size = ctx->chunk_csizes[i + 1]; entries[i] = cpu_to_le32(offset); offset += next_size; } } } else { aliased_le64_t *entries = (aliased_le64_t*)ctx->chunk_csizes; if (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_SOLID) { for (size_t i = 0; i < actual_num_chunk_entries; i++) entries[i] = cpu_to_le64(ctx->chunk_csizes[i]); } else { u64 offset = ctx->chunk_csizes[0]; for (size_t i = 0; i < actual_num_chunk_entries; i++) { u64 next_size = ctx->chunk_csizes[i + 1]; entries[i] = cpu_to_le64(offset); offset += next_size; } } } size_t chunk_table_size = actual_num_chunk_entries * chunk_entry_size; u64 res_start_offset; u64 res_end_offset; if (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_PIPABLE) { ret = full_write(ctx->out_fd, ctx->chunk_csizes, chunk_table_size); if (ret) goto write_error; res_end_offset = ctx->out_fd->offset; res_start_offset = ctx->chunks_start_offset; } else { res_end_offset = ctx->out_fd->offset; u64 chunk_table_offset; chunk_table_offset = ctx->chunks_start_offset - chunk_table_size; if (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_SOLID) { struct alt_chunk_table_header_disk hdr; hdr.res_usize = cpu_to_le64(res_actual_size); hdr.chunk_size = cpu_to_le32(ctx->out_chunk_size); hdr.compression_format = cpu_to_le32(ctx->out_ctype); STATIC_ASSERT(WIMLIB_COMPRESSION_TYPE_XPRESS == 1); STATIC_ASSERT(WIMLIB_COMPRESSION_TYPE_LZX == 2); STATIC_ASSERT(WIMLIB_COMPRESSION_TYPE_LZMS == 3); ret = full_pwrite(ctx->out_fd, &hdr, sizeof(hdr), chunk_table_offset - sizeof(hdr)); if (ret) goto write_error; res_start_offset = chunk_table_offset - sizeof(hdr); } else { res_start_offset = chunk_table_offset; } ret = full_pwrite(ctx->out_fd, ctx->chunk_csizes, chunk_table_size, chunk_table_offset); if (ret) goto write_error; } *res_start_offset_ret = res_start_offset; *res_store_size_ret = res_end_offset - res_start_offset; return 0; write_error: ERROR_WITH_ERRNO("Error writing chunk table to WIM file"); return ret; } /* Finish writing a WIM resource by writing or updating the chunk table (if not * writing the data uncompressed) and loading its metadata into @out_reshdr. */ static int end_write_resource(struct write_blobs_ctx *ctx, struct wim_reshdr *out_reshdr) { int ret; u64 res_size_in_wim; u64 res_uncompressed_size; u64 res_offset_in_wim; wimlib_assert(ctx->cur_write_blob_offset == ctx->cur_write_res_size || (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_SOLID)); res_uncompressed_size = ctx->cur_write_res_size; if (ctx->compressor) { ret = end_chunk_table(ctx, res_uncompressed_size, &res_offset_in_wim, &res_size_in_wim); if (ret) return ret; } else { res_offset_in_wim = ctx->chunks_start_offset; res_size_in_wim = ctx->out_fd->offset - res_offset_in_wim; } out_reshdr->uncompressed_size = res_uncompressed_size; out_reshdr->size_in_wim = res_size_in_wim; out_reshdr->offset_in_wim = res_offset_in_wim; return 0; } /* Call when no more data from the file at @path is needed. */ static int done_with_file(const tchar *path, wimlib_progress_func_t progfunc, void *progctx) { union wimlib_progress_info info; info.done_with_file.path_to_file = path; return call_progress(progfunc, WIMLIB_PROGRESS_MSG_DONE_WITH_FILE, &info, progctx); } static int do_done_with_blob(struct blob_descriptor *blob, wimlib_progress_func_t progfunc, void *progctx) { int ret; struct wim_inode *inode; const tchar *path; tchar *cookie1; tchar *cookie2; if (!blob->may_send_done_with_file) return 0; inode = blob->file_inode; wimlib_assert(inode != NULL); wimlib_assert(inode->i_num_remaining_streams > 0); if (--inode->i_num_remaining_streams > 0) return 0; path = blob_file_path(blob); cookie1 = progress_get_streamless_path(path); cookie2 = progress_get_win32_path(path); ret = done_with_file(path, progfunc, progctx); progress_put_win32_path(cookie2); progress_put_streamless_path(cookie1); return ret; } /* Handle WIMLIB_WRITE_FLAG_SEND_DONE_WITH_FILE_MESSAGES mode. */ static inline int done_with_blob(struct blob_descriptor *blob, struct write_blobs_ctx *ctx) { if (likely(!(ctx->write_resource_flags & WRITE_RESOURCE_FLAG_SEND_DONE_WITH_FILE))) return 0; return do_done_with_blob(blob, ctx->progress_data.progfunc, ctx->progress_data.progctx); } /* Begin processing a blob for writing. */ static int write_blob_begin_read(struct blob_descriptor *blob, void *_ctx) { struct write_blobs_ctx *ctx = _ctx; int ret; wimlib_assert(blob->size > 0); /* As an optimization, we allow some blobs to be "unhashed", meaning * their SHA-1 message digests are unknown. This is the case with blobs * that are added by scanning a directory tree with wimlib_add_image(), * for example. Since WIM uses single-instance blobs, we don't know * whether such each such blob really need to written until it is * actually checksummed, unless it has a unique size. In such cases we * read and checksum the blob in this function, thereby advancing ahead * of read_blob_list(), which will still provide the data again to * write_blob_process_chunk(). This is okay because an unhashed blob * cannot be in a WIM resource, which might be costly to decompress. */ if (ctx->blob_table != NULL && blob->unhashed && !blob->unique_size) { struct blob_descriptor *new_blob; ret = hash_unhashed_blob(blob, ctx->blob_table, &new_blob); if (ret) return ret; if (new_blob != blob) { /* Duplicate blob detected. */ if (new_blob->will_be_in_output_wim || blob_filtered(new_blob, ctx->filter_ctx)) { /* The duplicate blob is already being included * in the output WIM, or it would be filtered * out if it had been. Skip writing this blob * (and reading it again) entirely, passing its * output reference count to the duplicate blob * in the former case. */ ret = do_write_blobs_progress(&ctx->progress_data, blob->size, 1, true); list_del(&blob->write_blobs_list); list_del(&blob->blob_table_list); if (new_blob->will_be_in_output_wim) new_blob->out_refcnt += blob->out_refcnt; if (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_SOLID) ctx->cur_write_res_size -= blob->size; if (!ret) ret = done_with_blob(blob, ctx); free_blob_descriptor(blob); if (ret) return ret; return BEGIN_BLOB_STATUS_SKIP_BLOB; } else { /* The duplicate blob can validly be written, * but was not marked as such. Discard the * current blob descriptor and use the * duplicate, but actually freeing the current * blob descriptor must wait until * read_blob_list() has finished reading its * data. */ list_replace(&blob->write_blobs_list, &new_blob->write_blobs_list); list_replace(&blob->blob_table_list, &new_blob->blob_table_list); blob->will_be_in_output_wim = 0; new_blob->out_refcnt = blob->out_refcnt; new_blob->will_be_in_output_wim = 1; new_blob->may_send_done_with_file = 0; blob = new_blob; } } } list_move_tail(&blob->write_blobs_list, &ctx->blobs_being_compressed); return 0; } /* Rewrite a blob that was just written compressed (as a non-solid WIM resource) * as uncompressed instead. */ static int write_blob_uncompressed(struct blob_descriptor *blob, struct filedes *out_fd) { int ret; u64 begin_offset = blob->out_reshdr.offset_in_wim; u64 end_offset = out_fd->offset; if (filedes_seek(out_fd, begin_offset) == -1) return 0; ret = extract_blob_to_fd(blob, out_fd); if (ret) { /* Error reading the uncompressed data. */ if (out_fd->offset == begin_offset && filedes_seek(out_fd, end_offset) != -1) { /* Nothing was actually written yet, and we successfully * seeked to the end of the compressed resource, so * don't issue a hard error; just keep the compressed * resource instead. */ WARNING("Recovered compressed resource of " "size %"PRIu64", continuing on.", blob->size); return 0; } return ret; } wimlib_assert(out_fd->offset - begin_offset == blob->size); /* We could ftruncate() the file to 'out_fd->offset' here, but there * isn't much point. Usually we will only be truncating by a few bytes * and will just overwrite the data immediately. */ blob->out_reshdr.size_in_wim = blob->size; blob->out_reshdr.flags &= ~(WIM_RESHDR_FLAG_COMPRESSED | WIM_RESHDR_FLAG_SOLID); return 0; } /* Returns true if the specified blob, which was written as a non-solid * resource, should be truncated from the WIM file and re-written uncompressed. * blob->out_reshdr must be filled in from the initial write of the blob. */ static bool should_rewrite_blob_uncompressed(const struct write_blobs_ctx *ctx, const struct blob_descriptor *blob) { /* If the compressed data is smaller than the uncompressed data, prefer * the compressed data. */ if (blob->out_reshdr.size_in_wim < blob->out_reshdr.uncompressed_size) return false; /* If we're not actually writing compressed data, then there's no need * for re-writing. */ if (!ctx->compressor) return false; /* If writing a pipable WIM, everything we write to the output is final * (it might actually be a pipe!). */ if (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_PIPABLE) return false; /* If the blob that would need to be re-read is located in a solid * resource in another WIM file, then re-reading it would be costly. So * don't do it. * * Exception: if the compressed size happens to be *exactly* the same as * the uncompressed size, then the blob *must* be written uncompressed * in order to remain compatible with the Windows Overlay Filesystem * filter driver (WOF). * * TODO: we are currently assuming that the optimization for * single-chunk resources in maybe_rewrite_blob_uncompressed() prevents * this case from being triggered too often. To fully prevent excessive * decompressions in degenerate cases, we really should obtain the * uncompressed data by decompressing the compressed data we wrote to * the output file. */ if (blob->blob_location == BLOB_IN_WIM && blob->size != blob->rdesc->uncompressed_size && blob->size != blob->out_reshdr.size_in_wim) return false; return true; } static int maybe_rewrite_blob_uncompressed(struct write_blobs_ctx *ctx, struct blob_descriptor *blob) { if (!should_rewrite_blob_uncompressed(ctx, blob)) return 0; /* Regular (non-solid) WIM resources with exactly one chunk and * compressed size equal to uncompressed size are exactly the same as * the corresponding compressed data --- since there must be 0 entries * in the chunk table and the only chunk must be stored uncompressed. * In this case, there's no need to rewrite anything. */ if (ctx->chunk_index == 1 && blob->out_reshdr.size_in_wim == blob->out_reshdr.uncompressed_size) { blob->out_reshdr.flags &= ~WIM_RESHDR_FLAG_COMPRESSED; return 0; } return write_blob_uncompressed(blob, ctx->out_fd); } /* Write the next chunk of (typically compressed) data to the output WIM, * handling the writing of the chunk table. */ static int write_chunk(struct write_blobs_ctx *ctx, const void *cchunk, size_t csize, size_t usize) { int ret; struct blob_descriptor *blob; u32 completed_blob_count; u32 completed_size; blob = list_entry(ctx->blobs_being_compressed.next, struct blob_descriptor, write_blobs_list); if (ctx->cur_write_blob_offset == 0 && !(ctx->write_resource_flags & WRITE_RESOURCE_FLAG_SOLID)) { /* Starting to write a new blob in non-solid mode. */ if (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_PIPABLE) { ret = write_pwm_blob_header(blob, ctx->out_fd, ctx->compressor != NULL); if (ret) return ret; } ret = begin_write_resource(ctx, blob->size); if (ret) return ret; } if (ctx->compressor != NULL) { /* Record the compresed chunk size. */ wimlib_assert(ctx->chunk_index < ctx->num_alloc_chunks); ctx->chunk_csizes[ctx->chunk_index++] = csize; /* If writing a pipable WIM, before the chunk data write a chunk * header that provides the compressed chunk size. */ if (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_PIPABLE) { struct pwm_chunk_hdr chunk_hdr = { .compressed_size = cpu_to_le32(csize), }; ret = full_write(ctx->out_fd, &chunk_hdr, sizeof(chunk_hdr)); if (ret) goto write_error; } } /* Write the chunk data. */ ret = full_write(ctx->out_fd, cchunk, csize); if (ret) goto write_error; ctx->cur_write_blob_offset += usize; completed_size = usize; completed_blob_count = 0; if (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_SOLID) { /* Wrote chunk in solid mode. It may have finished multiple * blobs. */ struct blob_descriptor *next_blob; while (blob && ctx->cur_write_blob_offset >= blob->size) { ctx->cur_write_blob_offset -= blob->size; if (ctx->cur_write_blob_offset) next_blob = list_entry(blob->write_blobs_list.next, struct blob_descriptor, write_blobs_list); else next_blob = NULL; ret = done_with_blob(blob, ctx); if (ret) return ret; list_move_tail(&blob->write_blobs_list, &ctx->blobs_in_solid_resource); completed_blob_count++; blob = next_blob; } } else { /* Wrote chunk in non-solid mode. It may have finished a * blob. */ if (ctx->cur_write_blob_offset == blob->size) { wimlib_assert(ctx->cur_write_blob_offset == ctx->cur_write_res_size); ret = end_write_resource(ctx, &blob->out_reshdr); if (ret) return ret; blob->out_reshdr.flags = reshdr_flags_for_blob(blob); if (ctx->compressor != NULL) blob->out_reshdr.flags |= WIM_RESHDR_FLAG_COMPRESSED; ret = maybe_rewrite_blob_uncompressed(ctx, blob); if (ret) return ret; wimlib_assert(blob->out_reshdr.uncompressed_size == blob->size); ctx->cur_write_blob_offset = 0; ret = done_with_blob(blob, ctx); if (ret) return ret; list_del(&blob->write_blobs_list); completed_blob_count++; } } return do_write_blobs_progress(&ctx->progress_data, completed_size, completed_blob_count, false); write_error: ERROR_WITH_ERRNO("Error writing chunk data to WIM file"); return ret; } static int prepare_chunk_buffer(struct write_blobs_ctx *ctx) { /* While we are unable to get a new chunk buffer due to too many chunks * already outstanding, retrieve and write the next compressed chunk. */ while (!(ctx->cur_chunk_buf = ctx->compressor->get_chunk_buffer(ctx->compressor))) { const void *cchunk; u32 csize; u32 usize; bool bret; int ret; bret = ctx->compressor->get_compression_result(ctx->compressor, &cchunk, &csize, &usize); wimlib_assert(bret); ret = write_chunk(ctx, cchunk, csize, usize); if (ret) return ret; } return 0; } /* Process the next chunk of data to be written to a WIM resource. */ static int write_blob_process_chunk(const struct blob_descriptor *blob, u64 offset, const void *chunk, size_t size, void *_ctx) { struct write_blobs_ctx *ctx = _ctx; int ret; const u8 *chunkptr, *chunkend; wimlib_assert(size != 0); if (ctx->compressor == NULL) { /* Write chunk uncompressed. */ ret = write_chunk(ctx, chunk, size, size); if (ret) return ret; return 0; } /* Submit the chunk for compression, but take into account that the * @size the chunk was provided in may not correspond to the * @out_chunk_size being used for compression. */ chunkptr = chunk; chunkend = chunkptr + size; do { size_t needed_chunk_size; size_t bytes_consumed; if (!ctx->cur_chunk_buf) { ret = prepare_chunk_buffer(ctx); if (ret) return ret; } if (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_SOLID) { needed_chunk_size = ctx->out_chunk_size; } else { needed_chunk_size = min(ctx->out_chunk_size, ctx->cur_chunk_buf_filled + (blob->size - offset)); } bytes_consumed = min(chunkend - chunkptr, needed_chunk_size - ctx->cur_chunk_buf_filled); memcpy(&ctx->cur_chunk_buf[ctx->cur_chunk_buf_filled], chunkptr, bytes_consumed); chunkptr += bytes_consumed; offset += bytes_consumed; ctx->cur_chunk_buf_filled += bytes_consumed; if (ctx->cur_chunk_buf_filled == needed_chunk_size) { ctx->compressor->signal_chunk_filled(ctx->compressor, ctx->cur_chunk_buf_filled); ctx->cur_chunk_buf = NULL; ctx->cur_chunk_buf_filled = 0; } } while (chunkptr != chunkend); return 0; } /* Finish processing a blob for writing. It may not have been completely * written yet, as the chunk_compressor implementation may still have chunks * buffered or being compressed. */ static int write_blob_end_read(struct blob_descriptor *blob, int status, void *_ctx) { struct write_blobs_ctx *ctx = _ctx; if (!blob->will_be_in_output_wim) { /* The blob was a duplicate. Now that its data has finished * being read, it is being discarded in favor of the duplicate * entry. It therefore is no longer needed, and we can fire the * DONE_WITH_FILE callback because the file will not be read * again. * * Note: we can't yet fire DONE_WITH_FILE for non-duplicate * blobs, since it needs to be possible to re-read the file if * it does not compress to less than its original size. */ if (!status) status = done_with_blob(blob, ctx); free_blob_descriptor(blob); } else if (!status && blob->unhashed && ctx->blob_table != NULL) { /* The blob was not a duplicate and was previously unhashed. * Since we passed COMPUTE_MISSING_BLOB_HASHES to * read_blob_list(), blob->hash is now computed and valid. So * turn this blob into a "hashed" blob. */ list_del(&blob->unhashed_list); blob_table_insert(ctx->blob_table, blob); blob->unhashed = 0; } return status; } /* * Compute statistics about a list of blobs that will be written. * * Assumes the blobs are sorted such that all blobs located in each distinct WIM * (specified by WIMStruct) are together. * * For compactions, also verify that there are no overlapping resources. This * really should be checked earlier, but for now it's easiest to check here. */ static int compute_blob_list_stats(struct list_head *blob_list, struct write_blobs_ctx *ctx) { struct blob_descriptor *blob; u64 total_bytes = 0; u64 num_blobs = 0; u64 total_parts = 0; WIMStruct *prev_wim_part = NULL; const struct wim_resource_descriptor *prev_rdesc = NULL; list_for_each_entry(blob, blob_list, write_blobs_list) { num_blobs++; total_bytes += blob->size; if (blob->blob_location == BLOB_IN_WIM) { const struct wim_resource_descriptor *rdesc = blob->rdesc; WIMStruct *wim = rdesc->wim; if (prev_wim_part != wim) { prev_wim_part = wim; total_parts++; } if (unlikely(wim->being_compacted) && rdesc != prev_rdesc) { if (prev_rdesc != NULL && rdesc->offset_in_wim < prev_rdesc->offset_in_wim + prev_rdesc->size_in_wim) { WARNING("WIM file contains overlapping " "resources! Compaction is not " "possible."); return WIMLIB_ERR_RESOURCE_ORDER; } prev_rdesc = rdesc; } } } ctx->progress_data.progress.write_streams.total_bytes = total_bytes; ctx->progress_data.progress.write_streams.total_streams = num_blobs; ctx->progress_data.progress.write_streams.completed_bytes = 0; ctx->progress_data.progress.write_streams.completed_streams = 0; ctx->progress_data.progress.write_streams.compression_type = ctx->out_ctype; ctx->progress_data.progress.write_streams.total_parts = total_parts; ctx->progress_data.progress.write_streams.completed_parts = 0; ctx->progress_data.next_progress = 0; return 0; } /* Find blobs in @blob_list that can be copied to the output WIM in raw form * rather than compressed. Delete these blobs from @blob_list and move them to * @raw_copy_blobs. Return the total uncompressed size of the blobs that need * to be compressed. */ static u64 find_raw_copy_blobs(struct list_head *blob_list, int write_resource_flags, int out_ctype, u32 out_chunk_size, struct list_head *raw_copy_blobs) { struct blob_descriptor *blob, *tmp; u64 num_nonraw_bytes = 0; INIT_LIST_HEAD(raw_copy_blobs); /* Initialize temporary raw_copy_ok flag. */ list_for_each_entry(blob, blob_list, write_blobs_list) if (blob->blob_location == BLOB_IN_WIM) blob->rdesc->raw_copy_ok = 0; list_for_each_entry_safe(blob, tmp, blob_list, write_blobs_list) { if (can_raw_copy(blob, write_resource_flags, out_ctype, out_chunk_size)) { blob->rdesc->raw_copy_ok = 1; list_move_tail(&blob->write_blobs_list, raw_copy_blobs); } else { num_nonraw_bytes += blob->size; } } return num_nonraw_bytes; } /* Copy a raw compressed resource located in another WIM file to the WIM file * being written. */ static int write_raw_copy_resource(struct wim_resource_descriptor *in_rdesc, struct filedes *out_fd) { u64 cur_read_offset; u64 end_read_offset; u8 buf[BUFFER_SIZE]; size_t bytes_to_read; int ret; struct filedes *in_fd; struct blob_descriptor *blob; u64 out_offset_in_wim; /* Copy the raw data. */ cur_read_offset = in_rdesc->offset_in_wim; end_read_offset = cur_read_offset + in_rdesc->size_in_wim; out_offset_in_wim = out_fd->offset; if (in_rdesc->is_pipable) { if (cur_read_offset < sizeof(struct pwm_blob_hdr)) return WIMLIB_ERR_INVALID_PIPABLE_WIM; cur_read_offset -= sizeof(struct pwm_blob_hdr); out_offset_in_wim += sizeof(struct pwm_blob_hdr); } in_fd = &in_rdesc->wim->in_fd; wimlib_assert(cur_read_offset != end_read_offset); if (likely(!in_rdesc->wim->being_compacted) || in_rdesc->offset_in_wim > out_fd->offset) { do { bytes_to_read = min(sizeof(buf), end_read_offset - cur_read_offset); ret = full_pread(in_fd, buf, bytes_to_read, cur_read_offset); if (ret) { ERROR_WITH_ERRNO("Error reading raw data " "from WIM file"); return ret; } ret = full_write(out_fd, buf, bytes_to_read); if (ret) { ERROR_WITH_ERRNO("Error writing raw data " "to WIM file"); return ret; } cur_read_offset += bytes_to_read; } while (cur_read_offset != end_read_offset); } else { /* Optimization: the WIM file is being compacted and the * resource being written is already in the desired location. * Skip over the data instead of re-writing it. */ /* Due the earlier check for overlapping resources, it should * never be the case that we already overwrote the resource. */ wimlib_assert(!(in_rdesc->offset_in_wim < out_fd->offset)); if (-1 == filedes_seek(out_fd, out_fd->offset + in_rdesc->size_in_wim)) return WIMLIB_ERR_WRITE; } list_for_each_entry(blob, &in_rdesc->blob_list, rdesc_node) { if (blob->will_be_in_output_wim) { blob_set_out_reshdr_for_reuse(blob); if (in_rdesc->flags & WIM_RESHDR_FLAG_SOLID) blob->out_res_offset_in_wim = out_offset_in_wim; else blob->out_reshdr.offset_in_wim = out_offset_in_wim; } } return 0; } /* Copy a list of raw compressed resources located in other WIM file(s) to the * WIM file being written. */ static int write_raw_copy_resources(struct list_head *raw_copy_blobs, struct filedes *out_fd, struct write_blobs_progress_data *progress_data) { struct blob_descriptor *blob; int ret; list_for_each_entry(blob, raw_copy_blobs, write_blobs_list) blob->rdesc->raw_copy_ok = 1; list_for_each_entry(blob, raw_copy_blobs, write_blobs_list) { if (blob->rdesc->raw_copy_ok) { /* Write each solid resource only one time. */ ret = write_raw_copy_resource(blob->rdesc, out_fd); if (ret) return ret; blob->rdesc->raw_copy_ok = 0; } ret = do_write_blobs_progress(progress_data, blob->size, 1, false); if (ret) return ret; } return 0; } /* Wait for and write all chunks pending in the compressor. */ static int finish_remaining_chunks(struct write_blobs_ctx *ctx) { const void *cdata; u32 csize; u32 usize; int ret; if (ctx->compressor == NULL) return 0; if (ctx->cur_chunk_buf_filled != 0) { ctx->compressor->signal_chunk_filled(ctx->compressor, ctx->cur_chunk_buf_filled); } while (ctx->compressor->get_compression_result(ctx->compressor, &cdata, &csize, &usize)) { ret = write_chunk(ctx, cdata, csize, usize); if (ret) return ret; } return 0; } static void validate_blob_list(struct list_head *blob_list) { struct blob_descriptor *blob; list_for_each_entry(blob, blob_list, write_blobs_list) { wimlib_assert(blob->will_be_in_output_wim); wimlib_assert(blob->size != 0); } } static void init_done_with_file_info(struct list_head *blob_list) { struct blob_descriptor *blob; list_for_each_entry(blob, blob_list, write_blobs_list) { if (blob_is_in_file(blob)) { blob->file_inode->i_num_remaining_streams = 0; blob->may_send_done_with_file = 1; } else { blob->may_send_done_with_file = 0; } } list_for_each_entry(blob, blob_list, write_blobs_list) if (blob->may_send_done_with_file) blob->file_inode->i_num_remaining_streams++; } /* * Write a list of blobs to the output WIM file. * * @blob_list * The list of blobs to write, specified by a list of 'struct blob_descriptor' linked * by the 'write_blobs_list' member. * * @out_fd * The file descriptor, opened for writing, to which to write the blobs. * * @write_resource_flags * Flags to modify how the blobs are written: * * WRITE_RESOURCE_FLAG_RECOMPRESS: * Force compression of all resources, even if they could otherwise * be re-used by copying the raw data, due to being located in a WIM * file with compatible compression parameters. * * WRITE_RESOURCE_FLAG_PIPABLE: * Write the resources in the wimlib-specific pipable format, and * furthermore do so in such a way that no seeking backwards in * @out_fd will be performed (so it may be a pipe). * * WRITE_RESOURCE_FLAG_SOLID: * Combine all the blobs into a single resource rather than writing * them in separate resources. This flag is only valid if the WIM * version number has been, or will be, set to WIM_VERSION_SOLID. * This flag may not be combined with WRITE_RESOURCE_FLAG_PIPABLE. * * @out_ctype * Compression format to use in the output resources, specified as one of * the WIMLIB_COMPRESSION_TYPE_* constants. WIMLIB_COMPRESSION_TYPE_NONE * is allowed. * * @out_chunk_size * Compression chunk size to use in the output resources. It must be a * valid chunk size for the specified compression format @out_ctype, unless * @out_ctype is WIMLIB_COMPRESSION_TYPE_NONE, in which case this parameter * is ignored. * * @num_threads * Number of threads to use to compress data. If 0, a default number of * threads will be chosen. The number of threads still may be decreased * from the specified value if insufficient memory is detected. * * @blob_table * If on-the-fly deduplication of unhashed blobs is desired, this parameter * must be pointer to the blob table for the WIMStruct on whose behalf the * blobs are being written. Otherwise, this parameter can be NULL. * * @filter_ctx * If on-the-fly deduplication of unhashed blobs is desired, this parameter * can be a pointer to a context for blob filtering used to detect whether * the duplicate blob has been hard-filtered or not. If no blobs are * hard-filtered or no blobs are unhashed, this parameter can be NULL. * * This function will write the blobs in @blob_list to resources in * consecutive positions in the output WIM file, or to a single solid resource * if WRITE_RESOURCE_FLAG_SOLID was specified in @write_resource_flags. In both * cases, the @out_reshdr of the `struct blob_descriptor' for each blob written will be * updated to specify its location, size, and flags in the output WIM. In the * solid resource case, WIM_RESHDR_FLAG_SOLID will be set in the @flags field of * each @out_reshdr, and furthermore @out_res_offset_in_wim and * @out_res_size_in_wim of each @out_reshdr will be set to the offset and size, * respectively, in the output WIM of the solid resource containing the * corresponding blob. * * Each of the blobs to write may be in any location supported by the * resource-handling code (specifically, read_blob_list()), such as the contents * of external file that has been logically added to the output WIM, or a blob * in another WIM file that has been imported, or even a blob in the "same" WIM * file of which a modified copy is being written. In the case that a blob is * already in a WIM file and uses compatible compression parameters, by default * this function will re-use the raw data instead of decompressing it, then * recompressing it; however, with WRITE_RESOURCE_FLAG_RECOMPRESS * specified in @write_resource_flags, this is not done. * * As a further requirement, this function requires that the * @will_be_in_output_wim member be set to 1 on all blobs in @blob_list as well * as any other blobs not in @blob_list that will be in the output WIM file, but * set to 0 on any other blobs in the output WIM's blob table or sharing a solid * resource with a blob in @blob_list. Still furthermore, if on-the-fly * deduplication of blobs is possible, then all blobs in @blob_list must also be * linked by @blob_table_list along with any other blobs that have * @will_be_in_output_wim set. * * This function handles on-the-fly deduplication of blobs for which SHA-1 * message digests have not yet been calculated. Such blobs may or may not need * to be written. If @blob_table is non-NULL, then each blob in @blob_list that * has @unhashed set but not @unique_size set is checksummed immediately before * it would otherwise be read for writing in order to determine if it is * identical to another blob already being written or one that would be filtered * out of the output WIM using blob_filtered() with the context @filter_ctx. * Each such duplicate blob will be removed from @blob_list, its reference count * transferred to the pre-existing duplicate blob, its memory freed, and will * not be written. Alternatively, if a blob in @blob_list is a duplicate with * any blob in @blob_table that has not been marked for writing or would not be * hard-filtered, it is freed and the pre-existing duplicate is written instead, * taking ownership of the reference count and slot in the @blob_table_list. * * Returns 0 if every blob was either written successfully or did not need to be * written; otherwise returns a non-zero error code. */ static int write_blob_list(struct list_head *blob_list, struct filedes *out_fd, int write_resource_flags, int out_ctype, u32 out_chunk_size, unsigned num_threads, struct blob_table *blob_table, struct filter_context *filter_ctx, wimlib_progress_func_t progfunc, void *progctx) { int ret; struct write_blobs_ctx ctx; struct list_head raw_copy_blobs; u64 num_nonraw_bytes; wimlib_assert((write_resource_flags & (WRITE_RESOURCE_FLAG_SOLID | WRITE_RESOURCE_FLAG_PIPABLE)) != (WRITE_RESOURCE_FLAG_SOLID | WRITE_RESOURCE_FLAG_PIPABLE)); validate_blob_list(blob_list); if (list_empty(blob_list)) return 0; /* If needed, set auxiliary information so that we can detect when the * library has finished using each external file. */ if (unlikely(write_resource_flags & WRITE_RESOURCE_FLAG_SEND_DONE_WITH_FILE)) init_done_with_file_info(blob_list); memset(&ctx, 0, sizeof(ctx)); ctx.out_fd = out_fd; ctx.blob_table = blob_table; ctx.out_ctype = out_ctype; ctx.out_chunk_size = out_chunk_size; ctx.write_resource_flags = write_resource_flags; ctx.filter_ctx = filter_ctx; /* * We normally sort the blobs to write by a "sequential" order that is * optimized for reading. But when using solid compression, we instead * sort the blobs by file extension and file name (when applicable; and * we don't do this for blobs from solid resources) so that similar * files are grouped together, which improves the compression ratio. * This is somewhat of a hack since a blob does not necessarily * correspond one-to-one with a filename, nor is there any guarantee * that two files with similar names or extensions are actually similar * in content. A potential TODO is to sort the blobs based on some * measure of similarity of their actual contents. */ ret = sort_blob_list_by_sequential_order(blob_list, offsetof(struct blob_descriptor, write_blobs_list)); if (ret) return ret; ret = compute_blob_list_stats(blob_list, &ctx); if (ret) return ret; if (write_resource_flags & WRITE_RESOURCE_FLAG_SOLID_SORT) { ret = sort_blob_list_for_solid_compression(blob_list); if (unlikely(ret)) WARNING("Failed to sort blobs for solid compression. Continuing anyways."); } ctx.progress_data.progfunc = progfunc; ctx.progress_data.progctx = progctx; num_nonraw_bytes = find_raw_copy_blobs(blob_list, write_resource_flags, out_ctype, out_chunk_size, &raw_copy_blobs); /* Unless no data needs to be compressed, allocate a chunk_compressor to * do compression. There are serial and parallel implementations of the * chunk_compressor interface. We default to parallel using the * specified number of threads, unless the upper bound on the number * bytes needing to be compressed is less than a heuristic value. */ if (num_nonraw_bytes != 0 && out_ctype != WIMLIB_COMPRESSION_TYPE_NONE) { #ifdef ENABLE_MULTITHREADED_COMPRESSION if (num_nonraw_bytes > max(2000000, out_chunk_size)) { ret = new_parallel_chunk_compressor(out_ctype, out_chunk_size, num_threads, 0, &ctx.compressor); if (ret > 0) { WARNING("Couldn't create parallel chunk compressor: %"TS".\n" " Falling back to single-threaded compression.", wimlib_get_error_string(ret)); } } #endif if (ctx.compressor == NULL) { ret = new_serial_chunk_compressor(out_ctype, out_chunk_size, &ctx.compressor); if (ret) goto out_destroy_context; } } if (ctx.compressor) ctx.progress_data.progress.write_streams.num_threads = ctx.compressor->num_threads; else ctx.progress_data.progress.write_streams.num_threads = 1; ret = call_progress(ctx.progress_data.progfunc, WIMLIB_PROGRESS_MSG_WRITE_STREAMS, &ctx.progress_data.progress, ctx.progress_data.progctx); if (ret) goto out_destroy_context; /* Copy any compressed resources for which the raw data can be reused * without decompression. */ ret = write_raw_copy_resources(&raw_copy_blobs, ctx.out_fd, &ctx.progress_data); if (ret || num_nonraw_bytes == 0) goto out_destroy_context; INIT_LIST_HEAD(&ctx.blobs_being_compressed); if (write_resource_flags & WRITE_RESOURCE_FLAG_SOLID) { INIT_LIST_HEAD(&ctx.blobs_in_solid_resource); ret = begin_write_resource(&ctx, num_nonraw_bytes); if (ret) goto out_destroy_context; } /* Read the list of blobs needing to be compressed, using the specified * callbacks to execute processing of the data. */ struct read_blob_callbacks cbs = { .begin_blob = write_blob_begin_read, .continue_blob = write_blob_process_chunk, .end_blob = write_blob_end_read, .ctx = &ctx, }; ret = read_blob_list(blob_list, offsetof(struct blob_descriptor, write_blobs_list), &cbs, BLOB_LIST_ALREADY_SORTED | VERIFY_BLOB_HASHES | COMPUTE_MISSING_BLOB_HASHES); if (ret) goto out_destroy_context; ret = finish_remaining_chunks(&ctx); if (ret) goto out_destroy_context; if (write_resource_flags & WRITE_RESOURCE_FLAG_SOLID) { struct wim_reshdr reshdr; struct blob_descriptor *blob; u64 offset_in_res; ret = end_write_resource(&ctx, &reshdr); if (ret) goto out_destroy_context; offset_in_res = 0; list_for_each_entry(blob, &ctx.blobs_in_solid_resource, write_blobs_list) { blob->out_reshdr.size_in_wim = blob->size; blob->out_reshdr.flags = reshdr_flags_for_blob(blob) | WIM_RESHDR_FLAG_SOLID; blob->out_reshdr.uncompressed_size = 0; blob->out_reshdr.offset_in_wim = offset_in_res; blob->out_res_offset_in_wim = reshdr.offset_in_wim; blob->out_res_size_in_wim = reshdr.size_in_wim; blob->out_res_uncompressed_size = reshdr.uncompressed_size; offset_in_res += blob->size; } wimlib_assert(offset_in_res == reshdr.uncompressed_size); } out_destroy_context: FREE(ctx.chunk_csizes); if (ctx.compressor) ctx.compressor->destroy(ctx.compressor); return ret; } static int write_file_data_blobs(WIMStruct *wim, struct list_head *blob_list, int write_flags, unsigned num_threads, struct filter_context *filter_ctx) { int out_ctype; u32 out_chunk_size; int write_resource_flags; write_resource_flags = write_flags_to_resource_flags(write_flags); if (write_resource_flags & WRITE_RESOURCE_FLAG_SOLID) { out_chunk_size = wim->out_solid_chunk_size; out_ctype = wim->out_solid_compression_type; } else { out_chunk_size = wim->out_chunk_size; out_ctype = wim->out_compression_type; } return write_blob_list(blob_list, &wim->out_fd, write_resource_flags, out_ctype, out_chunk_size, num_threads, wim->blob_table, filter_ctx, wim->progfunc, wim->progctx); } /* Write the contents of the specified blob as a WIM resource. */ static int write_wim_resource(struct blob_descriptor *blob, struct filedes *out_fd, int out_ctype, u32 out_chunk_size, int write_resource_flags) { LIST_HEAD(blob_list); list_add(&blob->write_blobs_list, &blob_list); blob->will_be_in_output_wim = 1; return write_blob_list(&blob_list, out_fd, write_resource_flags & ~WRITE_RESOURCE_FLAG_SOLID, out_ctype, out_chunk_size, 1, NULL, NULL, NULL, NULL); } /* Write the contents of the specified buffer as a WIM resource. */ int write_wim_resource_from_buffer(const void *buf, size_t buf_size, bool is_metadata, struct filedes *out_fd, int out_ctype, u32 out_chunk_size, struct wim_reshdr *out_reshdr, u8 *hash_ret, int write_resource_flags) { int ret; struct blob_descriptor blob; if (unlikely(buf_size == 0)) { zero_reshdr(out_reshdr); if (hash_ret) copy_hash(hash_ret, zero_hash); return 0; } blob_set_is_located_in_attached_buffer(&blob, (void *)buf, buf_size); sha1_buffer(buf, buf_size, blob.hash); blob.unhashed = 0; blob.is_metadata = is_metadata; ret = write_wim_resource(&blob, out_fd, out_ctype, out_chunk_size, write_resource_flags); if (ret) return ret; copy_reshdr(out_reshdr, &blob.out_reshdr); if (hash_ret) copy_hash(hash_ret, blob.hash); return 0; } struct blob_size_table { struct hlist_head *array; size_t num_entries; size_t capacity; }; static int init_blob_size_table(struct blob_size_table *tab, size_t capacity) { tab->array = CALLOC(capacity, sizeof(tab->array[0])); if (tab->array == NULL) return WIMLIB_ERR_NOMEM; tab->num_entries = 0; tab->capacity = capacity; return 0; } static void destroy_blob_size_table(struct blob_size_table *tab) { FREE(tab->array); } static int blob_size_table_insert(struct blob_descriptor *blob, void *_tab) { struct blob_size_table *tab = _tab; size_t pos; struct blob_descriptor *same_size_blob; pos = hash_u64(blob->size) % tab->capacity; blob->unique_size = 1; hlist_for_each_entry(same_size_blob, &tab->array[pos], hash_list_2) { if (same_size_blob->size == blob->size) { blob->unique_size = 0; same_size_blob->unique_size = 0; break; } } hlist_add_head(&blob->hash_list_2, &tab->array[pos]); tab->num_entries++; return 0; } struct find_blobs_ctx { WIMStruct *wim; int write_flags; struct list_head blob_list; struct blob_size_table blob_size_tab; }; static void reference_blob_for_write(struct blob_descriptor *blob, struct list_head *blob_list, u32 nref) { if (!blob->will_be_in_output_wim) { blob->out_refcnt = 0; list_add_tail(&blob->write_blobs_list, blob_list); blob->will_be_in_output_wim = 1; } blob->out_refcnt += nref; } static int fully_reference_blob_for_write(struct blob_descriptor *blob, void *_blob_list) { struct list_head *blob_list = _blob_list; blob->will_be_in_output_wim = 0; reference_blob_for_write(blob, blob_list, blob->refcnt); return 0; } static int inode_find_blobs_to_reference(const struct wim_inode *inode, const struct blob_table *table, struct list_head *blob_list) { wimlib_assert(inode->i_nlink > 0); for (unsigned i = 0; i < inode->i_num_streams; i++) { struct blob_descriptor *blob; const u8 *hash; blob = stream_blob(&inode->i_streams[i], table); if (blob) { reference_blob_for_write(blob, blob_list, inode->i_nlink); } else { hash = stream_hash(&inode->i_streams[i]); if (!is_zero_hash(hash)) return blob_not_found_error(inode, hash); } } return 0; } static int do_blob_set_not_in_output_wim(struct blob_descriptor *blob, void *_ignore) { blob->will_be_in_output_wim = 0; return 0; } static int image_find_blobs_to_reference(WIMStruct *wim) { struct wim_image_metadata *imd; struct wim_inode *inode; struct blob_descriptor *blob; struct list_head *blob_list; int ret; imd = wim_get_current_image_metadata(wim); image_for_each_unhashed_blob(blob, imd) blob->will_be_in_output_wim = 0; blob_list = wim->private; image_for_each_inode(inode, imd) { ret = inode_find_blobs_to_reference(inode, wim->blob_table, blob_list); if (ret) return ret; } return 0; } static int prepare_unfiltered_list_of_blobs_in_output_wim(WIMStruct *wim, int image, int blobs_ok, struct list_head *blob_list_ret) { int ret; INIT_LIST_HEAD(blob_list_ret); if (blobs_ok && (image == WIMLIB_ALL_IMAGES || (image == 1 && wim->hdr.image_count == 1))) { /* Fast case: Assume that all blobs are being written and that * the reference counts are correct. */ struct blob_descriptor *blob; struct wim_image_metadata *imd; unsigned i; for_blob_in_table(wim->blob_table, fully_reference_blob_for_write, blob_list_ret); for (i = 0; i < wim->hdr.image_count; i++) { imd = wim->image_metadata[i]; image_for_each_unhashed_blob(blob, imd) fully_reference_blob_for_write(blob, blob_list_ret); } } else { /* Slow case: Walk through the images being written and * determine the blobs referenced. */ for_blob_in_table(wim->blob_table, do_blob_set_not_in_output_wim, NULL); wim->private = blob_list_ret; ret = for_image(wim, image, image_find_blobs_to_reference); if (ret) return ret; } return 0; } struct insert_other_if_hard_filtered_ctx { struct blob_size_table *tab; struct filter_context *filter_ctx; }; static int insert_other_if_hard_filtered(struct blob_descriptor *blob, void *_ctx) { struct insert_other_if_hard_filtered_ctx *ctx = _ctx; if (!blob->will_be_in_output_wim && blob_hard_filtered(blob, ctx->filter_ctx)) blob_size_table_insert(blob, ctx->tab); return 0; } static int determine_blob_size_uniquity(struct list_head *blob_list, struct blob_table *lt, struct filter_context *filter_ctx) { int ret; struct blob_size_table tab; struct blob_descriptor *blob; ret = init_blob_size_table(&tab, 9001); if (ret) return ret; if (may_hard_filter_blobs(filter_ctx)) { struct insert_other_if_hard_filtered_ctx ctx = { .tab = &tab, .filter_ctx = filter_ctx, }; for_blob_in_table(lt, insert_other_if_hard_filtered, &ctx); } list_for_each_entry(blob, blob_list, write_blobs_list) blob_size_table_insert(blob, &tab); destroy_blob_size_table(&tab); return 0; } static void filter_blob_list_for_write(struct list_head *blob_list, struct filter_context *filter_ctx) { struct blob_descriptor *blob, *tmp; list_for_each_entry_safe(blob, tmp, blob_list, write_blobs_list) { int status = blob_filtered(blob, filter_ctx); if (status == 0) { /* Not filtered. */ continue; } else { if (status > 0) { /* Soft filtered. */ } else { /* Hard filtered. */ blob->will_be_in_output_wim = 0; list_del(&blob->blob_table_list); } list_del(&blob->write_blobs_list); } } } /* * prepare_blob_list_for_write() - * * Prepare the list of blobs to write for writing a WIM containing the specified * image(s) with the specified write flags. * * @wim * The WIMStruct on whose behalf the write is occurring. * * @image * Image(s) from the WIM to write; may be WIMLIB_ALL_IMAGES. * * @write_flags * WIMLIB_WRITE_FLAG_* flags for the write operation: * * STREAMS_OK: For writes of all images, assume that all blobs in the blob * table of @wim and the per-image lists of unhashed blobs should be taken * as-is, and image metadata should not be searched for references. This * does not exclude filtering with APPEND and SKIP_EXTERNAL_WIMS, below. * * APPEND: Blobs already present in @wim shall not be returned in * @blob_list_ret. * * SKIP_EXTERNAL_WIMS: Blobs already present in a WIM file, but not @wim, * shall be returned in neither @blob_list_ret nor @blob_table_list_ret. * * @blob_list_ret * List of blobs, linked by write_blobs_list, that need to be written will * be returned here. * * Note that this function assumes that unhashed blobs will be written; it * does not take into account that they may become duplicates when actually * hashed. * * @blob_table_list_ret * List of blobs, linked by blob_table_list, that need to be included in * the WIM's blob table will be returned here. This will be a superset of * the blobs in @blob_list_ret. * * This list will be a proper superset of @blob_list_ret if and only if * WIMLIB_WRITE_FLAG_APPEND was specified in @write_flags and some of the * blobs that would otherwise need to be written were already located in * the WIM file. * * All blobs in this list will have @out_refcnt set to the number of * references to the blob in the output WIM. If * WIMLIB_WRITE_FLAG_STREAMS_OK was specified in @write_flags, @out_refcnt * may be as low as 0. * * @filter_ctx_ret * A context for queries of blob filter status with blob_filtered() is * returned in this location. * * In addition, @will_be_in_output_wim will be set to 1 in all blobs inserted * into @blob_table_list_ret and to 0 in all blobs in the blob table of @wim not * inserted into @blob_table_list_ret. * * Still furthermore, @unique_size will be set to 1 on all blobs in * @blob_list_ret that have unique size among all blobs in @blob_list_ret and * among all blobs in the blob table of @wim that are ineligible for being * written due to filtering. * * Returns 0 on success; nonzero on read error, memory allocation error, or * otherwise. */ static int prepare_blob_list_for_write(WIMStruct *wim, int image, int write_flags, struct list_head *blob_list_ret, struct list_head *blob_table_list_ret, struct filter_context *filter_ctx_ret) { int ret; struct blob_descriptor *blob; filter_ctx_ret->write_flags = write_flags; filter_ctx_ret->wim = wim; ret = prepare_unfiltered_list_of_blobs_in_output_wim( wim, image, write_flags & WIMLIB_WRITE_FLAG_STREAMS_OK, blob_list_ret); if (ret) return ret; INIT_LIST_HEAD(blob_table_list_ret); list_for_each_entry(blob, blob_list_ret, write_blobs_list) list_add_tail(&blob->blob_table_list, blob_table_list_ret); ret = determine_blob_size_uniquity(blob_list_ret, wim->blob_table, filter_ctx_ret); if (ret) return ret; if (may_filter_blobs(filter_ctx_ret)) filter_blob_list_for_write(blob_list_ret, filter_ctx_ret); return 0; } static int write_file_data(WIMStruct *wim, int image, int write_flags, unsigned num_threads, struct list_head *blob_list_override, struct list_head *blob_table_list_ret) { int ret; struct list_head _blob_list; struct list_head *blob_list; struct blob_descriptor *blob; struct filter_context _filter_ctx; struct filter_context *filter_ctx; if (blob_list_override == NULL) { /* Normal case: prepare blob list from image(s) being written. */ blob_list = &_blob_list; filter_ctx = &_filter_ctx; ret = prepare_blob_list_for_write(wim, image, write_flags, blob_list, blob_table_list_ret, filter_ctx); if (ret) return ret; } else { /* Currently only as a result of wimlib_split() being called: * use blob list already explicitly provided. Use existing * reference counts. */ blob_list = blob_list_override; filter_ctx = NULL; INIT_LIST_HEAD(blob_table_list_ret); list_for_each_entry(blob, blob_list, write_blobs_list) { blob->out_refcnt = blob->refcnt; blob->will_be_in_output_wim = 1; blob->unique_size = 0; list_add_tail(&blob->blob_table_list, blob_table_list_ret); } } return write_file_data_blobs(wim, blob_list, write_flags, num_threads, filter_ctx); } static int write_metadata_resources(WIMStruct *wim, int image, int write_flags) { int ret; int start_image; int end_image; int write_resource_flags; if (write_flags & WIMLIB_WRITE_FLAG_NO_METADATA) return 0; write_resource_flags = write_flags_to_resource_flags(write_flags); write_resource_flags &= ~WRITE_RESOURCE_FLAG_SOLID; ret = call_progress(wim->progfunc, WIMLIB_PROGRESS_MSG_WRITE_METADATA_BEGIN, NULL, wim->progctx); if (ret) return ret; if (image == WIMLIB_ALL_IMAGES) { start_image = 1; end_image = wim->hdr.image_count; } else { start_image = image; end_image = image; } for (int i = start_image; i <= end_image; i++) { struct wim_image_metadata *imd; imd = wim->image_metadata[i - 1]; if (is_image_dirty(imd)) { /* The image was modified from the original, or was * newly added, so we have to build and write a new * metadata resource. */ ret = write_metadata_resource(wim, i, write_resource_flags); } else if (is_image_unchanged_from_wim(imd, wim) && (write_flags & (WIMLIB_WRITE_FLAG_UNSAFE_COMPACT | WIMLIB_WRITE_FLAG_APPEND))) { /* The metadata resource is already in the WIM file. * For appends, we don't need to write it at all. For * compactions, we re-write existing metadata resources * along with the existing file resources, not here. */ if (write_flags & WIMLIB_WRITE_FLAG_APPEND) blob_set_out_reshdr_for_reuse(imd->metadata_blob); ret = 0; } else { /* The metadata resource is in a WIM file other than the * one being written to. We need to rewrite it, * possibly compressed differently; but rebuilding the * metadata itself isn't necessary. */ ret = write_wim_resource(imd->metadata_blob, &wim->out_fd, wim->out_compression_type, wim->out_chunk_size, write_resource_flags); } if (ret) return ret; } return call_progress(wim->progfunc, WIMLIB_PROGRESS_MSG_WRITE_METADATA_END, NULL, wim->progctx); } static int open_wim_writable(WIMStruct *wim, const tchar *path, int open_flags) { int raw_fd = topen(path, open_flags | O_BINARY, 0644); if (raw_fd < 0) { ERROR_WITH_ERRNO("Failed to open \"%"TS"\" for writing", path); return WIMLIB_ERR_OPEN; } filedes_init(&wim->out_fd, raw_fd); return 0; } static int close_wim_writable(WIMStruct *wim, int write_flags) { int ret = 0; if (!(write_flags & WIMLIB_WRITE_FLAG_FILE_DESCRIPTOR)) if (filedes_valid(&wim->out_fd)) if (filedes_close(&wim->out_fd)) ret = WIMLIB_ERR_WRITE; filedes_invalidate(&wim->out_fd); return ret; } static int cmp_blobs_by_out_rdesc(const void *p1, const void *p2) { const struct blob_descriptor *blob1, *blob2; blob1 = *(const struct blob_descriptor**)p1; blob2 = *(const struct blob_descriptor**)p2; if (blob1->out_reshdr.flags & WIM_RESHDR_FLAG_SOLID) { if (blob2->out_reshdr.flags & WIM_RESHDR_FLAG_SOLID) { if (blob1->out_res_offset_in_wim != blob2->out_res_offset_in_wim) return cmp_u64(blob1->out_res_offset_in_wim, blob2->out_res_offset_in_wim); } else { return 1; } } else { if (blob2->out_reshdr.flags & WIM_RESHDR_FLAG_SOLID) return -1; } return cmp_u64(blob1->out_reshdr.offset_in_wim, blob2->out_reshdr.offset_in_wim); } static int write_blob_table(WIMStruct *wim, int image, int write_flags, struct list_head *blob_table_list) { int ret; /* Set output resource metadata for blobs already present in WIM. */ if (write_flags & WIMLIB_WRITE_FLAG_APPEND) { struct blob_descriptor *blob; list_for_each_entry(blob, blob_table_list, blob_table_list) { if (blob->blob_location == BLOB_IN_WIM && blob->rdesc->wim == wim) { blob_set_out_reshdr_for_reuse(blob); } } } ret = sort_blob_list(blob_table_list, offsetof(struct blob_descriptor, blob_table_list), cmp_blobs_by_out_rdesc); if (ret) return ret; /* Add entries for metadata resources. */ if (!(write_flags & WIMLIB_WRITE_FLAG_NO_METADATA)) { int start_image; int end_image; if (image == WIMLIB_ALL_IMAGES) { start_image = 1; end_image = wim->hdr.image_count; } else { start_image = image; end_image = image; } /* Push metadata blob table entries onto the front of the list * in reverse order, so that they're written in order. */ for (int i = end_image; i >= start_image; i--) { struct blob_descriptor *metadata_blob; metadata_blob = wim->image_metadata[i - 1]->metadata_blob; wimlib_assert(metadata_blob->out_reshdr.flags & WIM_RESHDR_FLAG_METADATA); metadata_blob->out_refcnt = 1; list_add(&metadata_blob->blob_table_list, blob_table_list); } } return write_blob_table_from_blob_list(blob_table_list, &wim->out_fd, wim->out_hdr.part_number, &wim->out_hdr.blob_table_reshdr, write_flags_to_resource_flags(write_flags)); } /* * Finish writing a WIM file: write the blob table, xml data, and integrity * table, then overwrite the WIM header. * * The output file descriptor is closed on success, except when writing to a * user-specified file descriptor (WIMLIB_WRITE_FLAG_FILE_DESCRIPTOR set). */ static int finish_write(WIMStruct *wim, int image, int write_flags, struct list_head *blob_table_list) { int write_resource_flags; off_t old_blob_table_end = 0; struct integrity_table *old_integrity_table = NULL; off_t new_blob_table_end; u64 xml_totalbytes; int ret; write_resource_flags = write_flags_to_resource_flags(write_flags); /* In the WIM header, there is room for the resource entry for a * metadata resource labeled as the "boot metadata". This entry should * be zeroed out if there is no bootable image (boot_idx 0). Otherwise, * it should be a copy of the resource entry for the image that is * marked as bootable. */ if (wim->out_hdr.boot_idx == 0) { zero_reshdr(&wim->out_hdr.boot_metadata_reshdr); } else { copy_reshdr(&wim->out_hdr.boot_metadata_reshdr, &wim->image_metadata[ wim->out_hdr.boot_idx - 1]->metadata_blob->out_reshdr); } /* If appending to a WIM file containing an integrity table, we'd like * to re-use the information in the old integrity table instead of * recalculating it. But we might overwrite the old integrity table * when we expand the XML data. Read it into memory just in case. */ if ((write_flags & (WIMLIB_WRITE_FLAG_APPEND | WIMLIB_WRITE_FLAG_CHECK_INTEGRITY)) == (WIMLIB_WRITE_FLAG_APPEND | WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) && wim_has_integrity_table(wim)) { old_blob_table_end = wim->hdr.blob_table_reshdr.offset_in_wim + wim->hdr.blob_table_reshdr.size_in_wim; (void)read_integrity_table(wim, old_blob_table_end - WIM_HEADER_DISK_SIZE, &old_integrity_table); /* If we couldn't read the old integrity table, we can still * re-calculate the full integrity table ourselves. Hence the * ignoring of the return value. */ } /* Write blob table if needed. */ if (!(write_flags & WIMLIB_WRITE_FLAG_NO_NEW_BLOBS)) { ret = write_blob_table(wim, image, write_flags, blob_table_list); if (ret) goto out; } /* Write XML data. */ xml_totalbytes = wim->out_fd.offset; if (write_flags & WIMLIB_WRITE_FLAG_USE_EXISTING_TOTALBYTES) xml_totalbytes = WIM_TOTALBYTES_USE_EXISTING; ret = write_wim_xml_data(wim, image, xml_totalbytes, &wim->out_hdr.xml_data_reshdr, write_resource_flags); if (ret) goto out; /* Write integrity table if needed. */ if ((write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) && wim->out_hdr.blob_table_reshdr.offset_in_wim != 0) { if (write_flags & WIMLIB_WRITE_FLAG_NO_NEW_BLOBS) { /* The XML data we wrote may have overwritten part of * the old integrity table, so while calculating the new * integrity table we should temporarily update the WIM * header to remove the integrity table reference. */ struct wim_header checkpoint_hdr; memcpy(&checkpoint_hdr, &wim->out_hdr, sizeof(struct wim_header)); zero_reshdr(&checkpoint_hdr.integrity_table_reshdr); checkpoint_hdr.flags |= WIM_HDR_FLAG_WRITE_IN_PROGRESS; ret = write_wim_header(&checkpoint_hdr, &wim->out_fd, 0); if (ret) goto out; } new_blob_table_end = wim->out_hdr.blob_table_reshdr.offset_in_wim + wim->out_hdr.blob_table_reshdr.size_in_wim; ret = write_integrity_table(wim, new_blob_table_end, old_blob_table_end, old_integrity_table); if (ret) goto out; } else { /* No integrity table. */ zero_reshdr(&wim->out_hdr.integrity_table_reshdr); } /* Now that all information in the WIM header has been determined, the * preliminary header written earlier can be overwritten, the header of * the existing WIM file can be overwritten, or the final header can be * written to the end of the pipable WIM. */ wim->out_hdr.flags &= ~WIM_HDR_FLAG_WRITE_IN_PROGRESS; if (write_flags & WIMLIB_WRITE_FLAG_PIPABLE) ret = write_wim_header(&wim->out_hdr, &wim->out_fd, wim->out_fd.offset); else ret = write_wim_header(&wim->out_hdr, &wim->out_fd, 0); if (ret) goto out; ret = WIMLIB_ERR_WRITE; if (unlikely(write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT)) { /* Truncate any data the compaction freed up. */ if (ftruncate(wim->out_fd.fd, wim->out_fd.offset) && errno != EINVAL) /* allow compaction on untruncatable files, e.g. block devices */ { ERROR_WITH_ERRNO("Failed to truncate the output WIM file"); goto out; } } /* Possibly sync file data to disk before closing. On POSIX systems, it * is necessary to do this before using rename() to overwrite an * existing file with a new file. Otherwise, data loss would occur if * the system is abruptly terminated when the metadata for the rename * operation has been written to disk, but the new file data has not. */ ret = WIMLIB_ERR_WRITE; if (write_flags & WIMLIB_WRITE_FLAG_FSYNC) { if (fsync(wim->out_fd.fd)) { ERROR_WITH_ERRNO("Error syncing data to WIM file"); goto out; } } ret = WIMLIB_ERR_WRITE; if (close_wim_writable(wim, write_flags)) { ERROR_WITH_ERRNO("Failed to close the output WIM file"); goto out; } ret = 0; out: free_integrity_table(old_integrity_table); return ret; } #if defined(HAVE_SYS_FILE_H) && defined(HAVE_FLOCK) /* Set advisory lock on WIM file (if not already done so) */ int lock_wim_for_append(WIMStruct *wim) { if (wim->locked_for_append) return 0; if (!flock(wim->in_fd.fd, LOCK_EX | LOCK_NB)) { wim->locked_for_append = 1; return 0; } if (errno != EWOULDBLOCK) return 0; return WIMLIB_ERR_ALREADY_LOCKED; } /* Remove advisory lock on WIM file (if present) */ void unlock_wim_for_append(WIMStruct *wim) { if (wim->locked_for_append) { flock(wim->in_fd.fd, LOCK_UN); wim->locked_for_append = 0; } } #endif /* * write_pipable_wim(): * * Perform the intermediate stages of creating a "pipable" WIM (i.e. a WIM * capable of being applied from a pipe). * * Pipable WIMs are a wimlib-specific modification of the WIM format such that * images can be applied from them sequentially when the file data is sent over * a pipe. In addition, a pipable WIM can be written sequentially to a pipe. * The modifications made to the WIM format for pipable WIMs are: * * - Magic characters in header are "WLPWM\0\0\0" (wimlib pipable WIM) instead * of "MSWIM\0\0\0". This lets wimlib know that the WIM is pipable and also * stops other software from trying to read the file as a normal WIM. * * - The header at the beginning of the file does not contain all the normal * information; in particular it will have all 0's for the blob table and XML * data resource entries. This is because this information cannot be * determined until the blob table and XML data have been written. * Consequently, wimlib will write the full header at the very end of the * file. The header at the end, however, is only used when reading the WIM * from a seekable file (not a pipe). * * - An extra copy of the XML data is placed directly after the header. This * allows image names and sizes to be determined at an appropriate time when * reading the WIM from a pipe. This copy of the XML data is ignored if the * WIM is read from a seekable file (not a pipe). * * - Solid resources are not allowed. Each blob is always stored in its own * resource. * * - The format of resources, or blobs, has been modified to allow them to be * used before the "blob table" has been read. Each blob is prefixed with a * `struct pwm_blob_hdr' that is basically an abbreviated form of `struct * blob_descriptor_disk' that only contains the SHA-1 message digest, * uncompressed blob size, and flags that indicate whether the blob is * compressed. The data of uncompressed blobs then follows literally, while * the data of compressed blobs follows in a modified format. Compressed * blobs do not begin with a chunk table, since the chunk table cannot be * written until all chunks have been compressed. Instead, each compressed * chunk is prefixed by a `struct pwm_chunk_hdr' that gives its size. * Furthermore, the chunk table is written at the end of the resource instead * of the start. Note: chunk offsets are given in the chunk table as if the * `struct pwm_chunk_hdr's were not present; also, the chunk table is only * used if the WIM is being read from a seekable file (not a pipe). * * - Metadata blobs always come before non-metadata blobs. (This does not by * itself constitute an incompatibility with normal WIMs, since this is valid * in normal WIMs.) * * - At least up to the end of the blobs, all components must be packed as * tightly as possible; there cannot be any "holes" in the WIM. (This does * not by itself consititute an incompatibility with normal WIMs, since this * is valid in normal WIMs.) * * Note: the blob table, XML data, and header at the end are not used when * applying from a pipe. They exist to support functionality such as image * application and export when the WIM is *not* read from a pipe. * * Layout of pipable WIM: * * ---------+----------+--------------------+----------------+--------------+-----------+--------+ * | Header | XML data | Metadata resources | File resources | Blob table | XML data | Header | * ---------+----------+--------------------+----------------+--------------+-----------+--------+ * * Layout of normal WIM: * * +--------+-----------------------------+-------------------------+ * | Header | File and metadata resources | Blob table | XML data | * +--------+-----------------------------+-------------------------+ * * An optional integrity table can follow the final XML data in both normal and * pipable WIMs. However, due to implementation details, wimlib currently can * only include an integrity table in a pipable WIM when writing it to a * seekable file (not a pipe). * * Do note that since pipable WIMs are not supported by Microsoft's software, * wimlib does not create them unless explicitly requested (with * WIMLIB_WRITE_FLAG_PIPABLE) and as stated above they use different magic * characters to identify the file. */ static int write_pipable_wim(WIMStruct *wim, int image, int write_flags, unsigned num_threads, struct list_head *blob_list_override, struct list_head *blob_table_list_ret) { int ret; struct wim_reshdr xml_reshdr; WARNING("Creating a pipable WIM, which will " "be incompatible\n" " with Microsoft's software (WIMGAPI/ImageX/DISM)."); /* At this point, the header at the beginning of the file has already * been written. */ /* * For efficiency, wimlib normally delays calculating each newly added * stream's hash until while that stream being written, or just before * it is written. However, when writing a pipable WIM (potentially to a * pipe), we first have to write the metadata resources, which contain * all the hashes. Moreover each blob is prefixed with its hash (struct * pwm_blob_hdr). Thus, we have to calculate all the hashes before * writing anything. */ ret = wim_checksum_unhashed_blobs(wim); if (ret) return ret; /* Write extra copy of the XML data. */ ret = write_wim_xml_data(wim, image, WIM_TOTALBYTES_OMIT, &xml_reshdr, WRITE_RESOURCE_FLAG_PIPABLE); if (ret) return ret; /* Write metadata resources for the image(s) being included in the * output WIM. */ ret = write_metadata_resources(wim, image, write_flags); if (ret) return ret; /* Write file data needed for the image(s) being included in the output * WIM, or file data needed for the split WIM part. */ return write_file_data(wim, image, write_flags, num_threads, blob_list_override, blob_table_list_ret); /* The blob table, XML data, and header at end are handled by * finish_write(). */ } static bool should_default_to_solid_compression(WIMStruct *wim, int write_flags) { return wim->out_hdr.wim_version == WIM_VERSION_SOLID && !(write_flags & (WIMLIB_WRITE_FLAG_SOLID | WIMLIB_WRITE_FLAG_PIPABLE)) && wim_has_solid_resources(wim); } /* Update the images' filecount/bytecount stats (in the XML info) to take into * account any recent modifications. */ static int update_image_stats(WIMStruct *wim) { if (!wim_has_metadata(wim)) return 0; for (int i = 0; i < wim->hdr.image_count; i++) { struct wim_image_metadata *imd = wim->image_metadata[i]; if (imd->stats_outdated) { int ret = xml_update_image_info(wim, i + 1); if (ret) return ret; imd->stats_outdated = false; } } return 0; } /* Write a standalone WIM or split WIM (SWM) part to a new file or to a file * descriptor. */ int write_wim_part(WIMStruct *wim, const void *path_or_fd, int image, int write_flags, unsigned num_threads, unsigned part_number, unsigned total_parts, struct list_head *blob_list_override, const u8 *guid) { int ret; struct list_head blob_table_list; /* Internally, this is always called with a valid part number and total * parts. */ wimlib_assert(total_parts >= 1); wimlib_assert(part_number >= 1 && part_number <= total_parts); /* A valid image (or all images) must be specified. */ if (image != WIMLIB_ALL_IMAGES && (image < 1 || image > wim->hdr.image_count)) return WIMLIB_ERR_INVALID_IMAGE; /* If we need to write metadata resources, make sure the ::WIMStruct has * the needed information attached (e.g. is not a resource-only WIM, * such as a non-first part of a split WIM). */ if (!wim_has_metadata(wim) && !(write_flags & WIMLIB_WRITE_FLAG_NO_METADATA)) return WIMLIB_ERR_METADATA_NOT_FOUND; /* Check for contradictory flags. */ if ((write_flags & (WIMLIB_WRITE_FLAG_CHECK_INTEGRITY | WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY)) == (WIMLIB_WRITE_FLAG_CHECK_INTEGRITY | WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY)) return WIMLIB_ERR_INVALID_PARAM; if ((write_flags & (WIMLIB_WRITE_FLAG_PIPABLE | WIMLIB_WRITE_FLAG_NOT_PIPABLE)) == (WIMLIB_WRITE_FLAG_PIPABLE | WIMLIB_WRITE_FLAG_NOT_PIPABLE)) return WIMLIB_ERR_INVALID_PARAM; /* Only wimlib_overwrite() accepts UNSAFE_COMPACT. */ if (write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) return WIMLIB_ERR_INVALID_PARAM; /* Include an integrity table by default if no preference was given and * the WIM already had an integrity table. */ if (!(write_flags & (WIMLIB_WRITE_FLAG_CHECK_INTEGRITY | WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY))) { if (wim_has_integrity_table(wim)) write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; } /* Write a pipable WIM by default if no preference was given and the WIM * was already pipable. */ if (!(write_flags & (WIMLIB_WRITE_FLAG_PIPABLE | WIMLIB_WRITE_FLAG_NOT_PIPABLE))) { if (wim_is_pipable(wim)) write_flags |= WIMLIB_WRITE_FLAG_PIPABLE; } if ((write_flags & (WIMLIB_WRITE_FLAG_PIPABLE | WIMLIB_WRITE_FLAG_SOLID)) == (WIMLIB_WRITE_FLAG_PIPABLE | WIMLIB_WRITE_FLAG_SOLID)) { ERROR("Solid compression is unsupported in pipable WIMs"); return WIMLIB_ERR_INVALID_PARAM; } /* Start initializing the new file header. */ memset(&wim->out_hdr, 0, sizeof(wim->out_hdr)); /* Set the magic number. */ if (write_flags & WIMLIB_WRITE_FLAG_PIPABLE) wim->out_hdr.magic = PWM_MAGIC; else wim->out_hdr.magic = WIM_MAGIC; /* Set the version number. */ if ((write_flags & WIMLIB_WRITE_FLAG_SOLID) || wim->out_compression_type == WIMLIB_COMPRESSION_TYPE_LZMS) wim->out_hdr.wim_version = WIM_VERSION_SOLID; else wim->out_hdr.wim_version = WIM_VERSION_DEFAULT; /* Default to solid compression if it is valid in the chosen WIM file * format and the WIMStruct references any solid resources. This is * useful when exporting an image from a solid WIM. */ if (should_default_to_solid_compression(wim, write_flags)) write_flags |= WIMLIB_WRITE_FLAG_SOLID; /* Set the header flags. */ wim->out_hdr.flags = (wim->hdr.flags & (WIM_HDR_FLAG_RP_FIX | WIM_HDR_FLAG_READONLY)); if (total_parts != 1) wim->out_hdr.flags |= WIM_HDR_FLAG_SPANNED; if (wim->out_compression_type != WIMLIB_COMPRESSION_TYPE_NONE) { wim->out_hdr.flags |= WIM_HDR_FLAG_COMPRESSION; switch (wim->out_compression_type) { case WIMLIB_COMPRESSION_TYPE_XPRESS: wim->out_hdr.flags |= WIM_HDR_FLAG_COMPRESS_XPRESS; break; case WIMLIB_COMPRESSION_TYPE_LZX: wim->out_hdr.flags |= WIM_HDR_FLAG_COMPRESS_LZX; break; case WIMLIB_COMPRESSION_TYPE_LZMS: wim->out_hdr.flags |= WIM_HDR_FLAG_COMPRESS_LZMS; break; } } /* Set the chunk size. */ wim->out_hdr.chunk_size = wim->out_chunk_size; /* Set the GUID. */ if (write_flags & WIMLIB_WRITE_FLAG_RETAIN_GUID) guid = wim->hdr.guid; if (guid) copy_guid(wim->out_hdr.guid, guid); else generate_guid(wim->out_hdr.guid); /* Set the part number and total parts. */ wim->out_hdr.part_number = part_number; wim->out_hdr.total_parts = total_parts; /* Set the image count. */ if (image == WIMLIB_ALL_IMAGES) wim->out_hdr.image_count = wim->hdr.image_count; else wim->out_hdr.image_count = 1; /* Set the boot index. */ wim->out_hdr.boot_idx = 0; if (total_parts == 1) { if (image == WIMLIB_ALL_IMAGES) wim->out_hdr.boot_idx = wim->hdr.boot_idx; else if (image == wim->hdr.boot_idx) wim->out_hdr.boot_idx = 1; } /* Update image stats if needed. */ ret = update_image_stats(wim); if (ret) return ret; /* Set up the output file descriptor. */ if (write_flags & WIMLIB_WRITE_FLAG_FILE_DESCRIPTOR) { /* File descriptor was explicitly provided. */ filedes_init(&wim->out_fd, *(const int *)path_or_fd); if (!filedes_is_seekable(&wim->out_fd)) { /* The file descriptor is a pipe. */ ret = WIMLIB_ERR_INVALID_PARAM; if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) goto out_cleanup; if (write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) { ERROR("Can't include integrity check when " "writing pipable WIM to pipe!"); goto out_cleanup; } } } else { /* Filename of WIM to write was provided; open file descriptor * to it. */ ret = open_wim_writable(wim, (const tchar*)path_or_fd, O_TRUNC | O_CREAT | O_RDWR); if (ret) goto out_cleanup; } /* Write initial header. This is merely a "dummy" header since it * doesn't have resource entries filled in yet, so it will be * overwritten later (unless writing a pipable WIM). */ if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) wim->out_hdr.flags |= WIM_HDR_FLAG_WRITE_IN_PROGRESS; ret = write_wim_header(&wim->out_hdr, &wim->out_fd, wim->out_fd.offset); wim->out_hdr.flags &= ~WIM_HDR_FLAG_WRITE_IN_PROGRESS; if (ret) goto out_cleanup; /* Write file data and metadata resources. */ if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) { /* Default case: create a normal (non-pipable) WIM. */ ret = write_file_data(wim, image, write_flags, num_threads, blob_list_override, &blob_table_list); if (ret) goto out_cleanup; ret = write_metadata_resources(wim, image, write_flags); if (ret) goto out_cleanup; } else { /* Non-default case: create pipable WIM. */ ret = write_pipable_wim(wim, image, write_flags, num_threads, blob_list_override, &blob_table_list); if (ret) goto out_cleanup; } /* Write blob table, XML data, and (optional) integrity table. */ ret = finish_write(wim, image, write_flags, &blob_table_list); out_cleanup: (void)close_wim_writable(wim, write_flags); return ret; } /* Write a standalone WIM to a file or file descriptor. */ static int write_standalone_wim(WIMStruct *wim, const void *path_or_fd, int image, int write_flags, unsigned num_threads) { return write_wim_part(wim, path_or_fd, image, write_flags, num_threads, 1, 1, NULL, NULL); } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_write(WIMStruct *wim, const tchar *path, int image, int write_flags, unsigned num_threads) { if (write_flags & ~WIMLIB_WRITE_MASK_PUBLIC) return WIMLIB_ERR_INVALID_PARAM; if (path == NULL || path[0] == T('\0')) return WIMLIB_ERR_INVALID_PARAM; return write_standalone_wim(wim, path, image, write_flags, num_threads); } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_write_to_fd(WIMStruct *wim, int fd, int image, int write_flags, unsigned num_threads) { if (write_flags & ~WIMLIB_WRITE_MASK_PUBLIC) return WIMLIB_ERR_INVALID_PARAM; if (fd < 0) return WIMLIB_ERR_INVALID_PARAM; write_flags |= WIMLIB_WRITE_FLAG_FILE_DESCRIPTOR; return write_standalone_wim(wim, &fd, image, write_flags, num_threads); } /* Have there been any changes to images in the specified WIM, including updates * as well as deletions and additions of entire images, but excluding changes to * the XML document? */ static bool any_images_changed(WIMStruct *wim) { if (wim->image_deletion_occurred) return true; for (int i = 0; i < wim->hdr.image_count; i++) if (!is_image_unchanged_from_wim(wim->image_metadata[i], wim)) return true; return false; } static int check_resource_offset(struct blob_descriptor *blob, void *_wim) { const WIMStruct *wim = _wim; off_t end_offset = *(const off_t*)wim->private; if (blob->blob_location == BLOB_IN_WIM && blob->rdesc->wim == wim && blob->rdesc->offset_in_wim + blob->rdesc->size_in_wim > end_offset) return WIMLIB_ERR_RESOURCE_ORDER; return 0; } /* Make sure no file or metadata resources are located after the XML data (or * integrity table if present)--- otherwise we can't safely append to the WIM * file and we return WIMLIB_ERR_RESOURCE_ORDER. */ static int check_resource_offsets(WIMStruct *wim, off_t end_offset) { int ret; unsigned i; wim->private = &end_offset; ret = for_blob_in_table(wim->blob_table, check_resource_offset, wim); if (ret) return ret; for (i = 0; i < wim->hdr.image_count; i++) { ret = check_resource_offset(wim->image_metadata[i]->metadata_blob, wim); if (ret) return ret; } return 0; } static int free_blob_if_invalidated(struct blob_descriptor *blob, void *_wim) { const WIMStruct *wim = _wim; if (!blob->will_be_in_output_wim && blob->blob_location == BLOB_IN_WIM && blob->rdesc->wim == wim) { blob_table_unlink(wim->blob_table, blob); free_blob_descriptor(blob); } return 0; } /* * Overwrite a WIM, possibly appending new resources to it. * * A WIM looks like (or is supposed to look like) the following: * * Header (212 bytes) * Resources for metadata and files (variable size) * Blob table (variable size) * XML data (variable size) * Integrity table (optional) (variable size) * * If we are not adding any new files or metadata, then the blob table is * unchanged--- so we only need to overwrite the XML data, integrity table, and * header. This operation is potentially unsafe if the program is abruptly * terminated while the XML data or integrity table are being overwritten, but * before the new header has been written. To partially alleviate this problem, * we write a temporary header after the XML data has been written. This may * prevent the WIM from becoming corrupted if the program is terminated while * the integrity table is being calculated (but no guarantees, due to write * re-ordering...). * * If we are adding new blobs, including new file data as well as any metadata * for any new images, then the blob table needs to be changed, and those blobs * need to be written. In this case, we try to perform a safe update of the WIM * file by writing the blobs *after* the end of the previous WIM, then writing * the new blob table, XML data, and (optionally) integrity table following the * new blobs. This will produce a layout like the following: * * Header (212 bytes) * (OLD) Resources for metadata and files (variable size) * (OLD) Blob table (variable size) * (OLD) XML data (variable size) * (OLD) Integrity table (optional) (variable size) * (NEW) Resources for metadata and files (variable size) * (NEW) Blob table (variable size) * (NEW) XML data (variable size) * (NEW) Integrity table (optional) (variable size) * * At all points, the WIM is valid as nothing points to the new data yet. Then, * the header is overwritten to point to the new blob table, XML data, and * integrity table, to produce the following layout: * * Header (212 bytes) * Resources for metadata and files (variable size) * Nothing (variable size) * Resources for metadata and files (variable size) * Blob table (variable size) * XML data (variable size) * Integrity table (optional) (variable size) * * This function allows an image to be appended to a large WIM very quickly, and * is crash-safe except in the case of write re-ordering, but the disadvantage * is that a small hole is left in the WIM where the old blob table, xml data, * and integrity table were. (These usually only take up a small amount of * space compared to the blobs, however.) * * Finally, this function also supports "compaction" overwrites as an * alternative to the normal "append" overwrites described above. In a * compaction, data is written starting immediately from the end of the header. * All existing resources are written first, in order by file offset. New * resources are written afterwards, and at the end any extra data is truncated * from the file. The advantage of this approach is that is that the WIM file * ends up fully optimized, without any holes remaining. The main disadavantage * is that this operation is fundamentally unsafe and cannot be interrupted * without data corruption. Consequently, compactions are only ever done when * explicitly requested by the library user with the flag * WIMLIB_WRITE_FLAG_UNSAFE_COMPACT. (Another disadvantage is that a compaction * can be much slower than an append.) */ static int overwrite_wim_inplace(WIMStruct *wim, int write_flags, unsigned num_threads) { int ret; off_t old_wim_end; struct list_head blob_list; struct list_head blob_table_list; struct filter_context filter_ctx; /* Include an integrity table by default if no preference was given and * the WIM already had an integrity table. */ if (!(write_flags & (WIMLIB_WRITE_FLAG_CHECK_INTEGRITY | WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY))) if (wim_has_integrity_table(wim)) write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; /* Start preparing the updated file header. */ memcpy(&wim->out_hdr, &wim->hdr, sizeof(wim->out_hdr)); /* If using solid compression, the version number must be set to * WIM_VERSION_SOLID. */ if (write_flags & WIMLIB_WRITE_FLAG_SOLID) wim->out_hdr.wim_version = WIM_VERSION_SOLID; /* Default to solid compression if it is valid in the chosen WIM file * format and the WIMStruct references any solid resources. This is * useful when updating a solid WIM. */ if (should_default_to_solid_compression(wim, write_flags)) write_flags |= WIMLIB_WRITE_FLAG_SOLID; if (unlikely(write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT)) { /* In-place compaction */ WARNING("The WIM file \"%"TS"\" is being compacted in place.\n" " Do *not* interrupt the operation, or else " "the WIM file will be\n" " corrupted!", wim->filename); wim->being_compacted = 1; old_wim_end = WIM_HEADER_DISK_SIZE; ret = prepare_blob_list_for_write(wim, WIMLIB_ALL_IMAGES, write_flags, &blob_list, &blob_table_list, &filter_ctx); if (ret) goto out; /* Prevent new files from being deduplicated with existing blobs * in the WIM that we haven't decided to write. Such blobs will * be overwritten during the compaction. */ for_blob_in_table(wim->blob_table, free_blob_if_invalidated, wim); if (wim_has_metadata(wim)) { /* Add existing metadata resources to be compacted along * with the file resources. */ for (int i = 0; i < wim->hdr.image_count; i++) { struct wim_image_metadata *imd = wim->image_metadata[i]; if (is_image_unchanged_from_wim(imd, wim)) { fully_reference_blob_for_write(imd->metadata_blob, &blob_list); } } } } else { u64 old_blob_table_end, old_xml_begin, old_xml_end; /* Set additional flags for append. */ write_flags |= WIMLIB_WRITE_FLAG_APPEND | WIMLIB_WRITE_FLAG_STREAMS_OK; /* Make sure there is no data after the XML data, except * possibily an integrity table. If this were the case, then * this data would be overwritten. */ old_xml_begin = wim->hdr.xml_data_reshdr.offset_in_wim; old_xml_end = old_xml_begin + wim->hdr.xml_data_reshdr.size_in_wim; if (wim->hdr.blob_table_reshdr.offset_in_wim == 0) old_blob_table_end = WIM_HEADER_DISK_SIZE; else old_blob_table_end = wim->hdr.blob_table_reshdr.offset_in_wim + wim->hdr.blob_table_reshdr.size_in_wim; if (wim_has_integrity_table(wim) && wim->hdr.integrity_table_reshdr.offset_in_wim < old_xml_end) { WARNING("Didn't expect the integrity table to be " "before the XML data"); ret = WIMLIB_ERR_RESOURCE_ORDER; goto out; } if (old_blob_table_end > old_xml_begin) { WARNING("Didn't expect the blob table to be after " "the XML data"); ret = WIMLIB_ERR_RESOURCE_ORDER; goto out; } /* Set @old_wim_end, which indicates the point beyond which we * don't allow any file and metadata resources to appear without * returning WIMLIB_ERR_RESOURCE_ORDER (due to the fact that we * would otherwise overwrite these resources). */ if (!any_images_changed(wim)) { /* If no images have been modified, added, or deleted, * then a new blob table does not need to be written. * We shall write the new XML data and optional * integrity table immediately after the blob table. * Note that this may overwrite an existing integrity * table. */ old_wim_end = old_blob_table_end; write_flags |= WIMLIB_WRITE_FLAG_NO_NEW_BLOBS; } else if (wim_has_integrity_table(wim)) { /* Old WIM has an integrity table; begin writing new * blobs after it. */ old_wim_end = wim->hdr.integrity_table_reshdr.offset_in_wim + wim->hdr.integrity_table_reshdr.size_in_wim; } else { /* No existing integrity table; begin writing new blobs * after the old XML data. */ old_wim_end = old_xml_end; } ret = check_resource_offsets(wim, old_wim_end); if (ret) goto out; ret = prepare_blob_list_for_write(wim, WIMLIB_ALL_IMAGES, write_flags, &blob_list, &blob_table_list, &filter_ctx); if (ret) goto out; if (write_flags & WIMLIB_WRITE_FLAG_NO_NEW_BLOBS) wimlib_assert(list_empty(&blob_list)); } /* Update image stats if needed. */ ret = update_image_stats(wim); if (ret) goto out; ret = open_wim_writable(wim, wim->filename, O_RDWR); if (ret) goto out; ret = lock_wim_for_append(wim); if (ret) goto out_close_wim; /* Set WIM_HDR_FLAG_WRITE_IN_PROGRESS flag in header. */ wim->hdr.flags |= WIM_HDR_FLAG_WRITE_IN_PROGRESS; ret = write_wim_header_flags(wim->hdr.flags, &wim->out_fd); wim->hdr.flags &= ~WIM_HDR_FLAG_WRITE_IN_PROGRESS; if (ret) { ERROR_WITH_ERRNO("Error updating WIM header flags"); goto out_unlock_wim; } if (filedes_seek(&wim->out_fd, old_wim_end) == -1) { ERROR_WITH_ERRNO("Can't seek to end of WIM"); ret = WIMLIB_ERR_WRITE; goto out_restore_hdr; } ret = write_file_data_blobs(wim, &blob_list, write_flags, num_threads, &filter_ctx); if (ret) goto out_truncate; ret = write_metadata_resources(wim, WIMLIB_ALL_IMAGES, write_flags); if (ret) goto out_truncate; ret = finish_write(wim, WIMLIB_ALL_IMAGES, write_flags, &blob_table_list); if (ret) goto out_truncate; unlock_wim_for_append(wim); return 0; out_truncate: if (!(write_flags & (WIMLIB_WRITE_FLAG_NO_NEW_BLOBS | WIMLIB_WRITE_FLAG_UNSAFE_COMPACT))) { WARNING("Truncating \"%"TS"\" to its original size " "(%"PRIu64" bytes)", wim->filename, old_wim_end); if (ftruncate(wim->out_fd.fd, old_wim_end)) WARNING_WITH_ERRNO("Failed to truncate WIM file!"); } out_restore_hdr: (void)write_wim_header_flags(wim->hdr.flags, &wim->out_fd); out_unlock_wim: unlock_wim_for_append(wim); out_close_wim: (void)close_wim_writable(wim, write_flags); out: wim->being_compacted = 0; return ret; } static int overwrite_wim_via_tmpfile(WIMStruct *wim, int write_flags, unsigned num_threads) { size_t wim_name_len; int ret; /* Write the WIM to a temporary file in the same directory as the * original WIM. */ wim_name_len = tstrlen(wim->filename); tchar tmpfile[wim_name_len + 10]; tmemcpy(tmpfile, wim->filename, wim_name_len); get_random_alnum_chars(tmpfile + wim_name_len, 9); tmpfile[wim_name_len + 9] = T('\0'); ret = wimlib_write(wim, tmpfile, WIMLIB_ALL_IMAGES, write_flags | WIMLIB_WRITE_FLAG_FSYNC | WIMLIB_WRITE_FLAG_RETAIN_GUID, num_threads); if (ret) { tunlink(tmpfile); return ret; } if (filedes_valid(&wim->in_fd)) { filedes_close(&wim->in_fd); filedes_invalidate(&wim->in_fd); } /* Rename the new WIM file to the original WIM file. Note: on Windows * this actually calls win32_rename_replacement(), not _wrename(), so * that removing the existing destination file can be handled. */ ret = trename(tmpfile, wim->filename); if (ret) { ERROR_WITH_ERRNO("Failed to rename `%"TS"' to `%"TS"'", tmpfile, wim->filename); #ifdef __WIN32__ if (ret < 0) #endif { tunlink(tmpfile); } return WIMLIB_ERR_RENAME; } union wimlib_progress_info progress; progress.rename.from = tmpfile; progress.rename.to = wim->filename; return call_progress(wim->progfunc, WIMLIB_PROGRESS_MSG_RENAME, &progress, wim->progctx); } /* Determine if the specified WIM file may be updated in-place rather than by * writing and replacing it with an entirely new file. */ static bool can_overwrite_wim_inplace(const WIMStruct *wim, int write_flags) { /* REBUILD flag forces full rebuild. */ if (write_flags & WIMLIB_WRITE_FLAG_REBUILD) return false; /* Image deletions cause full rebuild by default. */ if (wim->image_deletion_occurred && !(write_flags & WIMLIB_WRITE_FLAG_SOFT_DELETE)) return false; /* Pipable WIMs cannot be updated in place, nor can a non-pipable WIM be * turned into a pipable WIM in-place. */ if (wim_is_pipable(wim) || (write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) return false; /* The default compression type and compression chunk size selected for * the output WIM must be the same as those currently used for the WIM. */ if (wim->compression_type != wim->out_compression_type) return false; if (wim->chunk_size != wim->out_chunk_size) return false; return true; } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_overwrite(WIMStruct *wim, int write_flags, unsigned num_threads) { int ret; u32 orig_hdr_flags; if (write_flags & ~WIMLIB_WRITE_MASK_PUBLIC) return WIMLIB_ERR_INVALID_PARAM; if (!wim->filename) return WIMLIB_ERR_NO_FILENAME; if (unlikely(write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT)) { /* * In UNSAFE_COMPACT mode: * - RECOMPRESS is forbidden * - REBUILD is ignored * - SOFT_DELETE and NO_SOLID_SORT are implied */ if (write_flags & WIMLIB_WRITE_FLAG_RECOMPRESS) return WIMLIB_ERR_COMPACTION_NOT_POSSIBLE; write_flags &= ~WIMLIB_WRITE_FLAG_REBUILD; write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE; write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT; } orig_hdr_flags = wim->hdr.flags; if (write_flags & WIMLIB_WRITE_FLAG_IGNORE_READONLY_FLAG) wim->hdr.flags &= ~WIM_HDR_FLAG_READONLY; ret = can_modify_wim(wim); wim->hdr.flags = orig_hdr_flags; if (ret) return ret; if (can_overwrite_wim_inplace(wim, write_flags)) { ret = overwrite_wim_inplace(wim, write_flags, num_threads); if (ret != WIMLIB_ERR_RESOURCE_ORDER) return ret; WARNING("Falling back to re-building entire WIM"); } if (write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) return WIMLIB_ERR_COMPACTION_NOT_POSSIBLE; return overwrite_wim_via_tmpfile(wim, write_flags, num_threads); } wimlib-1.13.1/src/timestamp.c0000644000175000017500000000727013272231267012752 00000000000000/* * timestamp.c * * Conversion between Windows NT timestamps and UNIX timestamps. */ /* * Copyright (C) 2012-2017 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib.h" /* for struct wimlib_timespec */ #include "wimlib/timestamp.h" /* * Timestamps in WIM files are Windows NT timestamps, or FILETIMEs: 64-bit * values storing the number of 100-nanosecond ticks since January 1, 1601. * * Note: UNIX timestamps are signed; Windows timestamps are not. Negative UNIX * timestamps represent times before 1970-01-01. When such a timestamp is * converted to a Windows timestamp, we can preserve the correct date provided * that it is not also before 1601-01-01. */ #define NANOSECONDS_PER_TICK 100 #define TICKS_PER_SECOND (1000000000 / NANOSECONDS_PER_TICK) #define TICKS_PER_MICROSECOND (TICKS_PER_SECOND / 1000000) /* * EPOCH_DISTANCE is the number of seconds separating the Windows NT and UNIX * epochs. This is equal to ((1970-1601)*365+89)*24*60*60. 89 is the number * of leap years between 1970 and 1601. */ #define EPOCH_DISTANCE 11644473600 /* Windows NT timestamps to UNIX timestamps */ time_t wim_timestamp_to_time_t(u64 timestamp) { return (timestamp / TICKS_PER_SECOND) - EPOCH_DISTANCE; } void wim_timestamp_to_wimlib_timespec(u64 timestamp, struct wimlib_timespec *wts, s32 *high_part_ret) { s64 sec = (timestamp / TICKS_PER_SECOND) - EPOCH_DISTANCE; wts->tv_sec = sec; wts->tv_nsec = (timestamp % TICKS_PER_SECOND) * NANOSECONDS_PER_TICK; if (sizeof(wts->tv_sec) == 4) *high_part_ret = sec >> 32; } #ifdef __WIN32__ static _unused_attribute void check_sizeof_time_t(void) { /* Windows builds should always be using 64-bit time_t now. */ STATIC_ASSERT(sizeof(time_t) == 8); } #else struct timeval wim_timestamp_to_timeval(u64 timestamp) { return (struct timeval) { .tv_sec = wim_timestamp_to_time_t(timestamp), .tv_usec = (timestamp % TICKS_PER_SECOND) / TICKS_PER_MICROSECOND, }; } struct timespec wim_timestamp_to_timespec(u64 timestamp) { return (struct timespec) { .tv_sec = wim_timestamp_to_time_t(timestamp), .tv_nsec = (timestamp % TICKS_PER_SECOND) * NANOSECONDS_PER_TICK, }; } /* UNIX timestamps to Windows NT timestamps */ u64 time_t_to_wim_timestamp(time_t t) { return ((u64)t + EPOCH_DISTANCE) * TICKS_PER_SECOND; } u64 timeval_to_wim_timestamp(const struct timeval *tv) { return time_t_to_wim_timestamp(tv->tv_sec) + (u32)tv->tv_usec * TICKS_PER_MICROSECOND; } u64 timespec_to_wim_timestamp(const struct timespec *ts) { return time_t_to_wim_timestamp(ts->tv_sec) + (u32)ts->tv_nsec / NANOSECONDS_PER_TICK; } /* Retrieve the current time as a WIM timestamp. */ u64 now_as_wim_timestamp(void) { struct timeval tv; gettimeofday(&tv, NULL); return timeval_to_wim_timestamp(&tv); } #endif /* !__WIN32__ */ /* Translate a WIM timestamp into a human-readable string. */ void wim_timestamp_to_str(u64 timestamp, tchar *buf, size_t len) { struct tm tm; time_t t = wim_timestamp_to_time_t(timestamp); gmtime_r(&t, &tm); tstrftime(buf, len, T("%a %b %d %H:%M:%S %Y UTC"), &tm); } wimlib-1.13.1/src/win32_replacements.c0000644000175000017500000004422413272231267014453 00000000000000/* * win32_replacements.c - Replacements for various functions not available on * Windows, such as fsync(). */ /* * Copyright (C) 2013-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef __WIN32__ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include /* for _get_osfhandle() */ #include #include "wimlib/win32_common.h" #include "wimlib/assert.h" #include "wimlib/glob.h" #include "wimlib/error.h" #include "wimlib/timestamp.h" #include "wimlib/util.h" static int win32_error_to_errno(DWORD err_code) { /* This mapping is that used in Cygwin. * Some of these choices are arbitrary. */ switch (err_code) { case ERROR_ACCESS_DENIED: return EACCES; case ERROR_ACTIVE_CONNECTIONS: return EAGAIN; case ERROR_ALREADY_EXISTS: return EEXIST; case ERROR_BAD_DEVICE: return ENODEV; case ERROR_BAD_EXE_FORMAT: return ENOEXEC; case ERROR_BAD_NETPATH: return ENOENT; case ERROR_BAD_NET_NAME: return ENOENT; case ERROR_BAD_NET_RESP: return ENOSYS; case ERROR_BAD_PATHNAME: return ENOENT; case ERROR_BAD_PIPE: return EINVAL; case ERROR_BAD_UNIT: return ENODEV; case ERROR_BAD_USERNAME: return EINVAL; case ERROR_BEGINNING_OF_MEDIA: return EIO; case ERROR_BROKEN_PIPE: return EPIPE; case ERROR_BUSY: return EBUSY; case ERROR_BUS_RESET: return EIO; case ERROR_CALL_NOT_IMPLEMENTED: return ENOSYS; case ERROR_CANNOT_MAKE: return EPERM; case ERROR_CHILD_NOT_COMPLETE: return EBUSY; case ERROR_COMMITMENT_LIMIT: return EAGAIN; case ERROR_CRC: return EIO; case ERROR_DEVICE_DOOR_OPEN: return EIO; case ERROR_DEVICE_IN_USE: return EAGAIN; case ERROR_DEVICE_REQUIRES_CLEANING: return EIO; case ERROR_DIRECTORY: return ENOTDIR; case ERROR_DIR_NOT_EMPTY: return ENOTEMPTY; case ERROR_DISK_CORRUPT: return EIO; case ERROR_DISK_FULL: return ENOSPC; #ifdef ENOTUNIQ case ERROR_DUP_NAME: return ENOTUNIQ; #endif case ERROR_EAS_DIDNT_FIT: return ENOSPC; #ifdef ENOTSUP case ERROR_EAS_NOT_SUPPORTED: return ENOTSUP; #endif case ERROR_EA_LIST_INCONSISTENT: return EINVAL; case ERROR_EA_TABLE_FULL: return ENOSPC; case ERROR_END_OF_MEDIA: return ENOSPC; case ERROR_EOM_OVERFLOW: return EIO; case ERROR_EXE_MACHINE_TYPE_MISMATCH: return ENOEXEC; case ERROR_EXE_MARKED_INVALID: return ENOEXEC; case ERROR_FILEMARK_DETECTED: return EIO; case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG; case ERROR_FILE_CORRUPT: return EEXIST; case ERROR_FILE_EXISTS: return EEXIST; case ERROR_FILE_INVALID: return ENXIO; case ERROR_FILE_NOT_FOUND: return ENOENT; case ERROR_HANDLE_DISK_FULL: return ENOSPC; #ifdef ENODATA case ERROR_HANDLE_EOF: return ENODATA; #endif case ERROR_INVALID_ADDRESS: return EINVAL; case ERROR_INVALID_AT_INTERRUPT_TIME: return EINTR; case ERROR_INVALID_BLOCK_LENGTH: return EIO; case ERROR_INVALID_DATA: return EINVAL; case ERROR_INVALID_DRIVE: return ENODEV; case ERROR_INVALID_EA_NAME: return EINVAL; case ERROR_INVALID_EXE_SIGNATURE: return ENOEXEC; #ifdef EBADRQC case ERROR_INVALID_FUNCTION: return EBADRQC; #endif case ERROR_INVALID_HANDLE: return EBADF; case ERROR_INVALID_NAME: return ENOENT; case ERROR_INVALID_PARAMETER: return EINVAL; case ERROR_INVALID_SIGNAL_NUMBER: return EINVAL; case ERROR_IOPL_NOT_ENABLED: return ENOEXEC; case ERROR_IO_DEVICE: return EIO; case ERROR_IO_INCOMPLETE: return EAGAIN; case ERROR_IO_PENDING: return EAGAIN; case ERROR_LOCK_VIOLATION: return EBUSY; case ERROR_MAX_THRDS_REACHED: return EAGAIN; case ERROR_META_EXPANSION_TOO_LONG: return EINVAL; case ERROR_MOD_NOT_FOUND: return ENOENT; #ifdef EMSGSIZE case ERROR_MORE_DATA: return EMSGSIZE; #endif case ERROR_NEGATIVE_SEEK: return EINVAL; case ERROR_NETNAME_DELETED: return ENOENT; case ERROR_NOACCESS: return EFAULT; case ERROR_NONE_MAPPED: return EINVAL; case ERROR_NONPAGED_SYSTEM_RESOURCES: return EAGAIN; #ifdef ENOLINK case ERROR_NOT_CONNECTED: return ENOLINK; #endif case ERROR_NOT_ENOUGH_MEMORY: return ENOMEM; case ERROR_NOT_OWNER: return EPERM; #ifdef ENOMEDIUM case ERROR_NOT_READY: return ENOMEDIUM; #endif case ERROR_NOT_SAME_DEVICE: return EXDEV; case ERROR_NOT_SUPPORTED: return ENOSYS; case ERROR_NO_DATA: return EPIPE; case ERROR_NO_DATA_DETECTED: return EIO; #ifdef ENOMEDIUM case ERROR_NO_MEDIA_IN_DRIVE: return ENOMEDIUM; #endif #ifdef ENMFILE case ERROR_NO_MORE_FILES: return ENMFILE; #endif #ifdef ENMFILE case ERROR_NO_MORE_ITEMS: return ENMFILE; #endif case ERROR_NO_MORE_SEARCH_HANDLES: return ENFILE; case ERROR_NO_PROC_SLOTS: return EAGAIN; case ERROR_NO_SIGNAL_SENT: return EIO; case ERROR_NO_SYSTEM_RESOURCES: return EFBIG; case ERROR_NO_TOKEN: return EINVAL; case ERROR_OPEN_FAILED: return EIO; case ERROR_OPEN_FILES: return EAGAIN; case ERROR_OUTOFMEMORY: return ENOMEM; case ERROR_PAGED_SYSTEM_RESOURCES: return EAGAIN; case ERROR_PAGEFILE_QUOTA: return EAGAIN; case ERROR_PATH_NOT_FOUND: return ENOENT; case ERROR_PIPE_BUSY: return EBUSY; case ERROR_PIPE_CONNECTED: return EBUSY; #ifdef ECOMM case ERROR_PIPE_LISTENING: return ECOMM; case ERROR_PIPE_NOT_CONNECTED: return ECOMM; #endif case ERROR_POSSIBLE_DEADLOCK: return EDEADLOCK; case ERROR_PRIVILEGE_NOT_HELD: return EPERM; case ERROR_PROCESS_ABORTED: return EFAULT; case ERROR_PROC_NOT_FOUND: return ESRCH; #ifdef ENONET case ERROR_REM_NOT_LIST: return ENONET; #endif case ERROR_SECTOR_NOT_FOUND: return EINVAL; case ERROR_SEEK: return EINVAL; case ERROR_SETMARK_DETECTED: return EIO; case ERROR_SHARING_BUFFER_EXCEEDED: return ENOLCK; case ERROR_SHARING_VIOLATION: return EBUSY; case ERROR_SIGNAL_PENDING: return EBUSY; case ERROR_SIGNAL_REFUSED: return EIO; #ifdef ELIBBAD case ERROR_SXS_CANT_GEN_ACTCTX: return ELIBBAD; #endif case ERROR_THREAD_1_INACTIVE: return EINVAL; case ERROR_TOO_MANY_LINKS: return EMLINK; case ERROR_TOO_MANY_OPEN_FILES: return EMFILE; case ERROR_WAIT_NO_CHILDREN: return ECHILD; case ERROR_WORKING_SET_QUOTA: return EAGAIN; case ERROR_WRITE_PROTECT: return EROFS; default: return -1; } } static void set_errno_from_win32_error(DWORD err) { errno = win32_error_to_errno(err); } static void set_errno_from_GetLastError(void) { set_errno_from_win32_error(GetLastError()); } /* Replacement for POSIX fsync() */ int fsync(int fd) { HANDLE h; h = (HANDLE)_get_osfhandle(fd); if (h == INVALID_HANDLE_VALUE) goto err; if (!FlushFileBuffers(h)) goto err_set_errno; return 0; err_set_errno: set_errno_from_GetLastError(); err: return -1; } /* Use the Win32 API to get the number of processors. */ unsigned get_available_cpus(void) { SYSTEM_INFO sysinfo; GetSystemInfo(&sysinfo); return sysinfo.dwNumberOfProcessors; } /* Use the Win32 API to get the amount of available memory. */ u64 get_available_memory(void) { MEMORYSTATUSEX status = { .dwLength = sizeof(status), }; GlobalMemoryStatusEx(&status); return (u64)min(status.ullTotalPhys, status.ullTotalVirtual) * 85 / 100; } /* Replacement for POSIX-2008 realpath(). Warning: partial functionality only * (resolved_path must be NULL). Also I highly doubt that GetFullPathName * really does the right thing under all circumstances. */ wchar_t * realpath(const wchar_t *path, wchar_t *resolved_path) { DWORD ret; DWORD err; wimlib_assert(resolved_path == NULL); ret = GetFullPathNameW(path, 0, NULL, NULL); if (!ret) { err = GetLastError(); goto fail_win32; } resolved_path = MALLOC(ret * sizeof(wchar_t)); if (!resolved_path) goto out; ret = GetFullPathNameW(path, ret, resolved_path, NULL); if (!ret) { err = GetLastError(); FREE(resolved_path); resolved_path = NULL; goto fail_win32; } goto out; fail_win32: set_errno_from_win32_error(err); out: return resolved_path; } /* A quick hack to get reasonable rename() semantics on Windows, in particular * deleting the destination file instead of failing with ERROR_FILE_EXISTS and * working around any processes that may have the destination file open. * * Note: This is intended to be called when overwriting a regular file with an * updated copy and is *not* a fully POSIX compliant rename(). For that you may * wish to take a look at Cygwin's implementation, but be prepared... * * Return 0 on success, -1 on regular error, or 1 if the destination file was * deleted but the source could not be renamed and therefore should not be * deleted. */ int win32_rename_replacement(const wchar_t *srcpath, const wchar_t *dstpath) { wchar_t *tmpname; /* Normally, MoveFileExW() with the MOVEFILE_REPLACE_EXISTING flag does * what we want. */ if (MoveFileExW(srcpath, dstpath, MOVEFILE_REPLACE_EXISTING)) return 0; /* MoveFileExW() failed. One way this can happen is if any process has * the destination file open, in which case ERROR_ACCESS_DENIED is * produced. This can commonly happen if there is a backup or antivirus * program monitoring or scanning the files. This behavior is very * different from the behavior of POSIX rename(), which simply unlinks * the destination file and allows other processes to keep it open! */ if (GetLastError() != ERROR_ACCESS_DENIED) goto err_set_errno; /* We can work around the above-mentioned problem by renaming the * destination file to yet another temporary file, then "deleting" it, * which on Windows will in fact not actually delete it immediately but * rather mark it for deletion when the last handle to it is closed. */ { static const wchar_t orig_suffix[5] = L".orig"; const size_t num_rand_chars = 9; wchar_t *p; size_t dstlen = wcslen(dstpath); tmpname = alloca(sizeof(wchar_t) * (dstlen + ARRAY_LEN(orig_suffix) + num_rand_chars + 1)); p = tmpname; p = wmempcpy(p, dstpath, dstlen); p = wmempcpy(p, orig_suffix, ARRAY_LEN(orig_suffix)); get_random_alnum_chars(p, num_rand_chars); p += num_rand_chars; *p = L'\0'; } if (!MoveFile(dstpath, tmpname)) goto err_set_errno; if (!DeleteFile(tmpname)) { set_errno_from_GetLastError(); WARNING_WITH_ERRNO("Failed to delete original file " "(moved to \"%ls\")", tmpname); } if (!MoveFile(srcpath, dstpath)) { set_errno_from_GetLastError(); WARNING_WITH_ERRNO("Atomic semantics not respected in " "failed rename() (new file is at \"%ls\")", srcpath); return 1; } return 0; err_set_errno: set_errno_from_GetLastError(); return -1; } /* This really could be replaced with _wcserror_s, but this doesn't seem to * actually be available in MSVCRT.DLL on Windows XP (perhaps it's statically * linked in by Visual Studio...?). */ int win32_strerror_r_replacement(int errnum, wchar_t *buf, size_t buflen) { static pthread_mutex_t strerror_lock = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&strerror_lock); mbstowcs(buf, strerror(errnum), buflen); buf[buflen - 1] = '\0'; pthread_mutex_unlock(&strerror_lock); return 0; } #define MAX_IO_AMOUNT 1048576 static int do_pread_or_pwrite(int fd, void *buf, size_t count, off_t offset, bool is_pwrite) { HANDLE h; LARGE_INTEGER orig_offset; DWORD result = 0xFFFFFFFF; LARGE_INTEGER relative_offset; OVERLAPPED overlapped; BOOL bret; DWORD err = 0; h = (HANDLE)_get_osfhandle(fd); if (h == INVALID_HANDLE_VALUE) goto error; if (GetFileType(h) == FILE_TYPE_PIPE) { errno = ESPIPE; goto error; } /* Get original position */ relative_offset.QuadPart = 0; if (!SetFilePointerEx(h, relative_offset, &orig_offset, FILE_CURRENT)) { err = GetLastError(); win32_error(err, L"Failed to get original file position"); goto error; } memset(&overlapped, 0, sizeof(overlapped)); overlapped.Offset = offset; overlapped.OffsetHigh = offset >> 32; /* Do the read or write at the specified offset */ count = min(count, MAX_IO_AMOUNT); SetLastError(0); if (is_pwrite) bret = WriteFile(h, buf, count, &result, &overlapped); else bret = ReadFile(h, buf, count, &result, &overlapped); if (!bret) { err = GetLastError(); win32_error(err, L"Failed to %s %zu bytes at offset %"PRIu64, (is_pwrite ? "write" : "read"), count, offset); goto error; } wimlib_assert(result <= count); /* Restore the original position */ if (!SetFilePointerEx(h, orig_offset, NULL, FILE_BEGIN)) { err = GetLastError(); win32_error(err, L"Failed to restore file position to %"PRIu64, offset); goto error; } return result; error: if (err) set_errno_from_win32_error(err); return -1; } /* Dumb Windows implementation of pread(). It temporarily changes the file * offset, so it is not safe to use with readers/writers on the same file * descriptor. */ ssize_t win32_pread(int fd, void *buf, size_t count, off_t offset) { return do_pread_or_pwrite(fd, buf, count, offset, false); } /* Dumb Windows implementation of pwrite(). It temporarily changes the file * offset, so it is not safe to use with readers/writers on the same file * descriptor. */ ssize_t win32_pwrite(int fd, const void *buf, size_t count, off_t offset) { return do_pread_or_pwrite(fd, (void*)buf, count, offset, true); } /* Replacement for read() which doesn't hide the Win32 error code */ ssize_t win32_read(int fd, void *buf, size_t count) { HANDLE h = (HANDLE)_get_osfhandle(fd); DWORD result = 0xFFFFFFFF; if (h == INVALID_HANDLE_VALUE) return -1; count = min(count, MAX_IO_AMOUNT); SetLastError(0); if (!ReadFile(h, buf, count, &result, NULL)) { DWORD err = GetLastError(); win32_error(err, L"Error reading %zu bytes from fd %d", count, fd); set_errno_from_win32_error(err); return -1; } wimlib_assert(result <= count); return result; } /* Replacement for write() which doesn't hide the Win32 error code */ ssize_t win32_write(int fd, const void *buf, size_t count) { HANDLE h = (HANDLE)_get_osfhandle(fd); DWORD result = 0xFFFFFFFF; if (h == INVALID_HANDLE_VALUE) return -1; count = min(count, MAX_IO_AMOUNT); SetLastError(0); if (!WriteFile(h, buf, count, &result, NULL)) { DWORD err = GetLastError(); win32_error(err, L"Error writing %zu bytes to fd %d", count, fd); set_errno_from_win32_error(err); return -1; } wimlib_assert(result <= count); return result; } /* Replacement for glob() in Windows native builds that operates on wide * characters. */ int win32_wglob(const wchar_t *pattern, int flags, int (*errfunc)(const wchar_t *epath, int eerrno), glob_t *pglob) { WIN32_FIND_DATAW dat; DWORD err; HANDLE hFind; int ret; size_t nspaces; int errno_save; const wchar_t *backslash, *end_slash; size_t prefix_len; backslash = wcsrchr(pattern, L'\\'); end_slash = wcsrchr(pattern, L'/'); if (backslash > end_slash) end_slash = backslash; if (end_slash) prefix_len = end_slash - pattern + 1; else prefix_len = 0; /* This function does not support all functionality of the POSIX glob(), * so make sure the parameters are consistent with supported * functionality. */ wimlib_assert(errfunc == NULL); wimlib_assert((flags & GLOB_ERR) == GLOB_ERR); wimlib_assert((flags & ~(GLOB_NOSORT | GLOB_ERR)) == 0); hFind = FindFirstFileW(pattern, &dat); if (hFind == INVALID_HANDLE_VALUE) { err = GetLastError(); if (err == ERROR_FILE_NOT_FOUND) { errno = 0; return GLOB_NOMATCH; } else { set_errno_from_win32_error(err); return GLOB_ABORTED; } } pglob->gl_pathc = 0; pglob->gl_pathv = NULL; nspaces = 0; do { wchar_t *path; if (pglob->gl_pathc == nspaces) { size_t new_nspaces; wchar_t **pathv; new_nspaces = nspaces * 2 + 1; pathv = REALLOC(pglob->gl_pathv, new_nspaces * sizeof(pglob->gl_pathv[0])); if (!pathv) goto oom; pglob->gl_pathv = pathv; nspaces = new_nspaces; } size_t filename_len = wcslen(dat.cFileName); size_t len_needed = prefix_len + filename_len; path = MALLOC((len_needed + 1) * sizeof(wchar_t)); if (!path) goto oom; wmemcpy(path, pattern, prefix_len); wmemcpy(path + prefix_len, dat.cFileName, filename_len + 1); pglob->gl_pathv[pglob->gl_pathc++] = path; } while (FindNextFileW(hFind, &dat)); err = GetLastError(); CloseHandle(hFind); if (err != ERROR_NO_MORE_FILES) { set_errno_from_win32_error(err); ret = GLOB_ABORTED; goto fail_globfree; } return 0; oom: CloseHandle(hFind); errno = ENOMEM; ret = GLOB_NOSPACE; fail_globfree: errno_save = errno; globfree(pglob); errno = errno_save; return ret; } void globfree(glob_t *pglob) { size_t i; for (i = 0; i < pglob->gl_pathc; i++) FREE(pglob->gl_pathv[i]); FREE(pglob->gl_pathv); } /* Replacement for fopen(path, "a") that doesn't prevent other processes from * reading the file */ FILE * win32_open_logfile(const wchar_t *path) { HANDLE h; int fd; FILE *fp; h = CreateFile(path, FILE_APPEND_DATA, FILE_SHARE_VALID_FLAGS, NULL, OPEN_ALWAYS, 0, NULL); if (h == INVALID_HANDLE_VALUE) return NULL; fd = _open_osfhandle((intptr_t)h, O_APPEND); if (fd < 0) { CloseHandle(h); return NULL; } fp = fdopen(fd, "a"); if (!fp) { close(fd); return NULL; } return fp; } #define RtlGenRandom SystemFunction036 BOOLEAN WINAPI RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength); /* * Generate @n cryptographically secure random bytes (thread-safe) * * This is the Windows version. It uses RtlGenRandom() (actually called * SystemFunction036) from advapi32.dll. */ void get_random_bytes(void *p, size_t n) { while (n != 0) { u32 count = min(n, UINT32_MAX); if (!RtlGenRandom(p, count)) { win32_error(GetLastError(), L"RtlGenRandom() failed (count=%u)", count); wimlib_assert(0); count = 0; } p += count; n -= count; } } /* Retrieve the current time as a WIM timestamp. */ u64 now_as_wim_timestamp(void) { FILETIME ft; GetSystemTimeAsFileTime(&ft); return ((u64)ft.dwHighDateTime << 32) | ft.dwLowDateTime; } #endif /* __WIN32__ */ wimlib-1.13.1/src/verify.c0000644000175000017500000001050213160354225012237 00000000000000/* * verify.c * * Verify WIM files. */ /* * Copyright (C) 2012, 2013, 2014 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib/blob_table.h" #include "wimlib/dentry.h" #include "wimlib/error.h" #include "wimlib/metadata.h" #include "wimlib/progress.h" #include "wimlib/security.h" static int append_blob_to_list(struct blob_descriptor *blob, void *_list) { list_add(&blob->extraction_list, (struct list_head *)_list); return 0; } struct verify_blob_list_ctx { wimlib_progress_func_t progfunc; void *progctx; union wimlib_progress_info *progress; u64 next_progress; }; static int verify_continue_blob(const struct blob_descriptor *blob, u64 offset, const void *chunk, size_t size, void *_ctx) { struct verify_blob_list_ctx *ctx = _ctx; union wimlib_progress_info *progress = ctx->progress; if (offset + size == blob->size) progress->verify_streams.completed_streams++; progress->verify_streams.completed_bytes += size; if (progress->verify_streams.completed_bytes >= ctx->next_progress) { int ret = call_progress(ctx->progfunc, WIMLIB_PROGRESS_MSG_VERIFY_STREAMS, progress, ctx->progctx); if (ret) return ret; set_next_progress(progress->verify_streams.completed_bytes, progress->verify_streams.total_bytes, &ctx->next_progress); } return 0; } static int verify_file_data_present(struct wim_image_metadata *imd, struct blob_table *blob_table) { struct wim_inode *inode; int ret; image_for_each_inode(inode, imd) { ret = inode_resolve_streams(inode, blob_table, false); if (ret) return ret; } return 0; } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_verify_wim(WIMStruct *wim, int verify_flags) { int ret; LIST_HEAD(blob_list); union wimlib_progress_info progress; struct verify_blob_list_ctx ctx; struct blob_descriptor *blob; struct read_blob_callbacks cbs = { .continue_blob = verify_continue_blob, .ctx = &ctx, }; /* Check parameters */ if (!wim) return WIMLIB_ERR_INVALID_PARAM; if (verify_flags) return WIMLIB_ERR_INVALID_PARAM; /* Verify the images */ if (wim_has_metadata(wim)) { memset(&progress, 0, sizeof(progress)); progress.verify_image.wimfile = wim->filename; progress.verify_image.total_images = wim->hdr.image_count; for (int i = 1; i <= wim->hdr.image_count; i++) { progress.verify_image.current_image = i; ret = call_progress(wim->progfunc, WIMLIB_PROGRESS_MSG_BEGIN_VERIFY_IMAGE, &progress, wim->progctx); if (ret) return ret; ret = select_wim_image(wim, i); if (ret) return ret; ret = verify_file_data_present(wim_get_current_image_metadata(wim), wim->blob_table); if (ret) return ret; ret = call_progress(wim->progfunc, WIMLIB_PROGRESS_MSG_END_VERIFY_IMAGE, &progress, wim->progctx); if (ret) return ret; } } else { WARNING("\"%"TS"\" does not contain image metadata. Skipping image verification.", wim->filename); } /* Verify the blobs: SHA-1 message digests must match */ for_blob_in_table(wim->blob_table, append_blob_to_list, &blob_list); memset(&progress, 0, sizeof(progress)); progress.verify_streams.wimfile = wim->filename; list_for_each_entry(blob, &blob_list, extraction_list) { progress.verify_streams.total_streams++; progress.verify_streams.total_bytes += blob->size; } ctx.progfunc = wim->progfunc; ctx.progctx = wim->progctx; ctx.progress = &progress; ctx.next_progress = 0; ret = call_progress(ctx.progfunc, WIMLIB_PROGRESS_MSG_VERIFY_STREAMS, ctx.progress, ctx.progctx); if (ret) return ret; return read_blob_list(&blob_list, offsetof(struct blob_descriptor, extraction_list), &cbs, VERIFY_BLOB_HASHES); } wimlib-1.13.1/src/lcpit_matchfinder.c0000644000175000017500000006343713454550566014445 00000000000000/* * lcpit_matchfinder.c * * A match-finder for Lempel-Ziv compression based on bottom-up construction and * traversal of the Longest Common Prefix (LCP) interval tree. * * The following copying information applies to this specific source code file: * * Written in 2014-2015 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include "wimlib/divsufsort.h" #include "wimlib/lcpit_matchfinder.h" #include "wimlib/util.h" #define LCP_BITS 6 #define LCP_MAX (((u32)1 << LCP_BITS) - 1) #define LCP_SHIFT (32 - LCP_BITS) #define LCP_MASK (LCP_MAX << LCP_SHIFT) #define POS_MASK (((u32)1 << (32 - LCP_BITS)) - 1) #define MAX_NORMAL_BUFSIZE (POS_MASK + 1) #define HUGE_LCP_BITS 7 #define HUGE_LCP_MAX (((u32)1 << HUGE_LCP_BITS) - 1) #define HUGE_LCP_SHIFT (64 - HUGE_LCP_BITS) #define HUGE_LCP_MASK ((u64)HUGE_LCP_MAX << HUGE_LCP_SHIFT) #define HUGE_POS_MASK 0xFFFFFFFF #define MAX_HUGE_BUFSIZE ((u64)HUGE_POS_MASK + 1) #define HUGE_UNVISITED_TAG 0x100000000 #define PREFETCH_SAFETY 5 /* * Build the LCP (Longest Common Prefix) array in linear time. * * LCP[r] will be the length of the longest common prefix between the suffixes * with positions SA[r - 1] and SA[r]. LCP[0] will be undefined. * * Algorithm taken from Kasai et al. (2001), but modified slightly: * * - With bytes there is no realistic way to reserve a unique symbol for * end-of-buffer, so use explicit checks for end-of-buffer. * * - For decreased memory usage and improved memory locality, pack the two * logically distinct SA and LCP arrays into a single array SA_and_LCP. * * - Since SA_and_LCP is accessed randomly, improve the cache behavior by * reading several entries ahead in ISA and prefetching the upcoming * SA_and_LCP entry. * * - If an LCP value is less than the minimum match length, then store 0. This * avoids having to do comparisons against the minimum match length later. * * - If an LCP value is greater than the "nice match length", then store the * "nice match length". This caps the number of bits needed to store each * LCP value, and this caps the depth of the LCP-interval tree, without * usually hurting the compression ratio too much. * * References: * * Kasai et al. 2001. Linear-Time Longest-Common-Prefix Computation in * Suffix Arrays and Its Applications. CPM '01 Proceedings of the 12th * Annual Symposium on Combinatorial Pattern Matching pp. 181-192. */ static void build_LCP(u32 SA_and_LCP[restrict], const u32 ISA[restrict], const u8 T[restrict], const u32 n, const u32 min_lcp, const u32 max_lcp) { u32 h = 0; for (u32 i = 0; i < n; i++) { const u32 r = ISA[i]; prefetchw(&SA_and_LCP[ISA[i + PREFETCH_SAFETY]]); if (r > 0) { const u32 j = SA_and_LCP[r - 1] & POS_MASK; const u32 lim = min(n - i, n - j); while (h < lim && T[i + h] == T[j + h]) h++; u32 stored_lcp = h; if (stored_lcp < min_lcp) stored_lcp = 0; else if (stored_lcp > max_lcp) stored_lcp = max_lcp; SA_and_LCP[r] |= stored_lcp << LCP_SHIFT; if (h > 0) h--; } } } /* * Use the suffix array accompanied with the longest-common-prefix array --- * which in combination can be called the "enhanced suffix array" --- to * simulate a bottom-up traversal of the corresponding suffix tree, or * equivalently the lcp-interval tree. Do so in suffix rank order, but save the * superinterval references needed for later bottom-up traversal of the tree in * suffix position order. * * To enumerate the lcp-intervals, this algorithm scans the suffix array and its * corresponding LCP array linearly. While doing so, it maintains a stack of * lcp-intervals that are currently open, meaning that their left boundaries * have been seen but their right boundaries have not. The bottom of the stack * is the interval which covers the entire suffix array (this has lcp=0), and * the top of the stack is the deepest interval that is currently open (this has * the greatest lcp of any interval on the stack). When this algorithm opens an * lcp-interval, it assigns it a unique index in intervals[] and pushes it onto * the stack. When this algorithm closes an interval, it pops it from the stack * and sets the intervals[] entry of that interval to the index and lcp of that * interval's superinterval, which is the new top of the stack. * * This algorithm also set pos_data[pos] for each suffix position 'pos' to the * index and lcp of the deepest lcp-interval containing it. Alternatively, we * can interpret each suffix as being associated with a singleton lcp-interval, * or leaf of the suffix tree. With this interpretation, an entry in pos_data[] * is the superinterval reference for one of these singleton lcp-intervals and * therefore is not fundamentally different from an entry in intervals[]. * * To reduce memory usage, this algorithm re-uses the suffix array's memory to * store the generated intervals[] array. This is possible because SA and LCP * are accessed linearly, and no more than one interval is generated per suffix. * * The techniques used in this algorithm are described in various published * papers. The generation of lcp-intervals from the suffix array (SA) and the * longest-common-prefix array (LCP) is given as Algorithm BottomUpTraverse in * Kasai et al. (2001) and Algorithm 4.1 ("Computation of lcp-intervals") in * Abouelhoda et al. (2004). Both these papers note the equivalence between * lcp-intervals (including the singleton lcp-interval for each suffix) and * nodes of the suffix tree. Abouelhoda et al. (2004) furthermore applies * bottom-up traversal of the lcp-interval tree to Lempel-Ziv factorization, as * does Chen at al. (2008). Algorithm CPS1b of Chen et al. (2008) dynamically * re-uses the suffix array during bottom-up traversal of the lcp-interval tree. * * References: * * Kasai et al. Linear-Time Longest-Common-Prefix Computation in Suffix * Arrays and Its Applications. 2001. CPM '01 Proceedings of the 12th * Annual Symposium on Combinatorial Pattern Matching pp. 181-192. * * M.I. Abouelhoda, S. Kurtz, E. Ohlebusch. 2004. Replacing Suffix Trees * With Enhanced Suffix Arrays. Journal of Discrete Algorithms Volume 2 * Issue 1, March 2004, pp. 53-86. * * G. Chen, S.J. Puglisi, W.F. Smyth. 2008. Lempel-Ziv Factorization * Using Less Time & Space. Mathematics in Computer Science June 2008, * Volume 1, Issue 4, pp. 605-623. */ static void build_LCPIT(u32 intervals[restrict], u32 pos_data[restrict], const u32 n) { u32 * const SA_and_LCP = intervals; u32 next_interval_idx; u32 open_intervals[LCP_MAX + 1]; u32 *top = open_intervals; u32 prev_pos = SA_and_LCP[0] & POS_MASK; *top = 0; intervals[0] = 0; next_interval_idx = 1; for (u32 r = 1; r < n; r++) { const u32 next_pos = SA_and_LCP[r] & POS_MASK; const u32 next_lcp = SA_and_LCP[r] & LCP_MASK; const u32 top_lcp = *top & LCP_MASK; prefetchw(&pos_data[SA_and_LCP[r + PREFETCH_SAFETY] & POS_MASK]); if (next_lcp == top_lcp) { /* Continuing the deepest open interval */ pos_data[prev_pos] = *top; } else if (next_lcp > top_lcp) { /* Opening a new interval */ *++top = next_lcp | next_interval_idx++; pos_data[prev_pos] = *top; } else { /* Closing the deepest open interval */ pos_data[prev_pos] = *top; for (;;) { const u32 closed_interval_idx = *top-- & POS_MASK; const u32 superinterval_lcp = *top & LCP_MASK; if (next_lcp == superinterval_lcp) { /* Continuing the superinterval */ intervals[closed_interval_idx] = *top; break; } else if (next_lcp > superinterval_lcp) { /* Creating a new interval that is a * superinterval of the one being * closed, but still a subinterval of * its superinterval */ *++top = next_lcp | next_interval_idx++; intervals[closed_interval_idx] = *top; break; } else { /* Also closing the superinterval */ intervals[closed_interval_idx] = *top; } } } prev_pos = next_pos; } /* Close any still-open intervals. */ pos_data[prev_pos] = *top; for (; top > open_intervals; top--) intervals[*top & POS_MASK] = *(top - 1); } /* * Advance the LCP-interval tree matchfinder by one byte. * * If @record_matches is true, then matches are written to the @matches array * sorted by strictly decreasing length and strictly decreasing offset, and the * return value is the number of matches found. Otherwise, @matches is ignored * and the return value is always 0. * * How this algorithm works: * * 'cur_pos' is the position of the current suffix, which is the suffix being * matched against. 'cur_pos' starts at 0 and is incremented each time this * function is called. This function finds each suffix with position less than * 'cur_pos' that shares a prefix with the current suffix, but for each distinct * prefix length it finds only the suffix with the greatest position (i.e. the * most recently seen in the linear traversal by position). This function * accomplishes this using the lcp-interval tree data structure that was built * by build_LCPIT() and is updated dynamically by this function. * * The first step is to read 'pos_data[cur_pos]', which gives the index and lcp * value of the deepest lcp-interval containing the current suffix --- or, * equivalently, the parent of the conceptual singleton lcp-interval that * contains the current suffix. * * The second step is to ascend the lcp-interval tree until reaching an interval * that has not yet been visited, and link the intervals to the current suffix * along the way. An lcp-interval has been visited if and only if it has been * linked to a suffix. Initially, no lcp-intervals are linked to suffixes. * * The third step is to continue ascending the lcp-interval tree, but indirectly * through suffix links rather than through the original superinterval * references, and continuing to form links with the current suffix along the * way. Each suffix visited during this step, except in a special case to * handle outdated suffixes, is a match which can be written to matches[]. Each * intervals[] entry contains the position of the next suffix to visit, which we * shall call 'match_pos'; this is the most recently seen suffix that belongs to * that lcp-interval. 'pos_data[match_pos]' then contains the lcp and interval * index of the next lcp-interval that should be visited. * * We can view these arrays as describing a new set of links that gets overlaid * on top of the original superinterval references of the lcp-interval tree. * Each such link can connect a node of the lcp-interval tree to an ancestor * more than one generation removed. * * For each one-byte advance, the current position becomes the most recently * seen suffix for a continuous sequence of lcp-intervals from a leaf interval * to the root interval. Conceptually, this algorithm needs to update all these * nodes to link to 'cur_pos', and then update 'pos_data[cur_pos]' to a "null" * link. But actually, if some of these nodes have the same most recently seen * suffix, then this algorithm just visits the pos_data[] entry for that suffix * and skips over all these nodes in one step. Updating the extra nodes is * accomplished by creating a redirection from the previous suffix to the * current suffix. * * Using this shortcutting scheme, it's possible for a suffix to become out of * date, which means that it is no longer the most recently seen suffix for the * lcp-interval under consideration. This case is detected by noticing when the * next lcp-interval link actually points deeper in the tree, and it is worked * around by just continuing until we get to a link that actually takes us * higher in the tree. This can be described as a lazy-update scheme. */ static forceinline u32 lcpit_advance_one_byte(const u32 cur_pos, u32 pos_data[restrict], u32 intervals[restrict], u32 next[restrict], struct lz_match matches[restrict], const bool record_matches) { u32 ref; u32 super_ref; u32 match_pos; struct lz_match *matchptr; /* Get the deepest lcp-interval containing the current suffix. */ ref = pos_data[cur_pos]; /* Prefetch upcoming data, up to 3 positions ahead. Assume the * intervals are already visited. */ /* Prefetch the superinterval via a suffix link for the deepest * lcp-interval containing the suffix starting 1 position from now. */ prefetchw(&intervals[pos_data[next[0]] & POS_MASK]); /* Prefetch suffix link for the deepest lcp-interval containing the * suffix starting 2 positions from now. */ next[0] = intervals[next[1]] & POS_MASK; prefetchw(&pos_data[next[0]]); /* Prefetch the deepest lcp-interval containing the suffix starting 3 * positions from now. */ next[1] = pos_data[cur_pos + 3] & POS_MASK; prefetchw(&intervals[next[1]]); /* There is no "next suffix" after the current one. */ pos_data[cur_pos] = 0; /* Ascend until we reach a visited interval, the root, or a child of the * root. Link unvisited intervals to the current suffix as we go. */ while ((super_ref = intervals[ref & POS_MASK]) & LCP_MASK) { intervals[ref & POS_MASK] = cur_pos; ref = super_ref; } if (super_ref == 0) { /* In this case, the current interval may be any of: * (1) the root; * (2) an unvisited child of the root; * (3) an interval last visited by suffix 0 * * We could avoid the ambiguity with (3) by using an lcp * placeholder value other than 0 to represent "visited", but * it's fastest to use 0. So we just don't allow matches with * position 0. */ if (ref != 0) /* Not the root? */ intervals[ref & POS_MASK] = cur_pos; return 0; } /* Ascend indirectly via pos_data[] links. */ match_pos = super_ref; matchptr = matches; for (;;) { while ((super_ref = pos_data[match_pos]) > ref) match_pos = intervals[super_ref & POS_MASK]; intervals[ref & POS_MASK] = cur_pos; pos_data[match_pos] = ref; if (record_matches) { matchptr->length = ref >> LCP_SHIFT; matchptr->offset = cur_pos - match_pos; matchptr++; } if (super_ref == 0) break; ref = super_ref; match_pos = intervals[ref & POS_MASK]; } return matchptr - matches; } /* Expand SA from 32 bits to 64 bits. */ static void expand_SA(void *p, u32 n) { typedef u32 _may_alias_attribute aliased_u32_t; typedef u64 _may_alias_attribute aliased_u64_t; aliased_u32_t *SA = p; aliased_u64_t *SA64 = p; u32 r = n - 1; do { SA64[r] = SA[r]; } while (r--); } /* Like build_LCP(), but for buffers larger than MAX_NORMAL_BUFSIZE. */ static void build_LCP_huge(u64 SA_and_LCP64[restrict], const u32 ISA[restrict], const u8 T[restrict], const u32 n, const u32 min_lcp, const u32 max_lcp) { u32 h = 0; for (u32 i = 0; i < n; i++) { const u32 r = ISA[i]; prefetchw(&SA_and_LCP64[ISA[i + PREFETCH_SAFETY]]); if (r > 0) { const u32 j = SA_and_LCP64[r - 1] & HUGE_POS_MASK; const u32 lim = min(n - i, n - j); while (h < lim && T[i + h] == T[j + h]) h++; u32 stored_lcp = h; if (stored_lcp < min_lcp) stored_lcp = 0; else if (stored_lcp > max_lcp) stored_lcp = max_lcp; SA_and_LCP64[r] |= (u64)stored_lcp << HUGE_LCP_SHIFT; if (h > 0) h--; } } } /* * Like build_LCPIT(), but for buffers larger than MAX_NORMAL_BUFSIZE. * * This "huge" version is also slightly different in that the lcp value stored * in each intervals[] entry is the lcp value for that interval, not its * superinterval. This lcp value stays put in intervals[] and doesn't get moved * to pos_data[] during lcpit_advance_one_byte_huge(). One consequence of this * is that we have to use a special flag to distinguish visited from unvisited * intervals. But overall, this scheme keeps the memory usage at 12n instead of * 16n. (The non-huge version is 8n.) */ static void build_LCPIT_huge(u64 intervals64[restrict], u32 pos_data[restrict], const u32 n) { u64 * const SA_and_LCP64 = intervals64; u32 next_interval_idx; u32 open_intervals[HUGE_LCP_MAX + 1]; u32 *top = open_intervals; u32 prev_pos = SA_and_LCP64[0] & HUGE_POS_MASK; *top = 0; intervals64[0] = 0; next_interval_idx = 1; for (u32 r = 1; r < n; r++) { const u32 next_pos = SA_and_LCP64[r] & HUGE_POS_MASK; const u64 next_lcp = SA_and_LCP64[r] & HUGE_LCP_MASK; const u64 top_lcp = intervals64[*top]; prefetchw(&pos_data[SA_and_LCP64[r + PREFETCH_SAFETY] & HUGE_POS_MASK]); if (next_lcp == top_lcp) { /* Continuing the deepest open interval */ pos_data[prev_pos] = *top; } else if (next_lcp > top_lcp) { /* Opening a new interval */ intervals64[next_interval_idx] = next_lcp; pos_data[prev_pos] = next_interval_idx; *++top = next_interval_idx++; } else { /* Closing the deepest open interval */ pos_data[prev_pos] = *top; for (;;) { const u32 closed_interval_idx = *top--; const u64 superinterval_lcp = intervals64[*top]; if (next_lcp == superinterval_lcp) { /* Continuing the superinterval */ intervals64[closed_interval_idx] |= HUGE_UNVISITED_TAG | *top; break; } else if (next_lcp > superinterval_lcp) { /* Creating a new interval that is a * superinterval of the one being * closed, but still a subinterval of * its superinterval */ intervals64[next_interval_idx] = next_lcp; intervals64[closed_interval_idx] |= HUGE_UNVISITED_TAG | next_interval_idx; *++top = next_interval_idx++; break; } else { /* Also closing the superinterval */ intervals64[closed_interval_idx] |= HUGE_UNVISITED_TAG | *top; } } } prev_pos = next_pos; } /* Close any still-open intervals. */ pos_data[prev_pos] = *top; for (; top > open_intervals; top--) intervals64[*top] |= HUGE_UNVISITED_TAG | *(top - 1); } /* Like lcpit_advance_one_byte(), but for buffers larger than * MAX_NORMAL_BUFSIZE. */ static forceinline u32 lcpit_advance_one_byte_huge(const u32 cur_pos, u32 pos_data[restrict], u64 intervals64[restrict], u32 prefetch_next[restrict], struct lz_match matches[restrict], const bool record_matches) { u32 interval_idx; u32 next_interval_idx; u64 cur; u64 next; u32 match_pos; struct lz_match *matchptr; interval_idx = pos_data[cur_pos]; prefetchw(&intervals64[pos_data[prefetch_next[0]] & HUGE_POS_MASK]); prefetch_next[0] = intervals64[prefetch_next[1]] & HUGE_POS_MASK; prefetchw(&pos_data[prefetch_next[0]]); prefetch_next[1] = pos_data[cur_pos + 3] & HUGE_POS_MASK; prefetchw(&intervals64[prefetch_next[1]]); pos_data[cur_pos] = 0; while ((next = intervals64[interval_idx]) & HUGE_UNVISITED_TAG) { intervals64[interval_idx] = (next & HUGE_LCP_MASK) | cur_pos; interval_idx = next & HUGE_POS_MASK; } matchptr = matches; while (next & HUGE_LCP_MASK) { cur = next; do { match_pos = next & HUGE_POS_MASK; next_interval_idx = pos_data[match_pos]; next = intervals64[next_interval_idx]; } while (next > cur); intervals64[interval_idx] = (cur & HUGE_LCP_MASK) | cur_pos; pos_data[match_pos] = interval_idx; if (record_matches) { matchptr->length = cur >> HUGE_LCP_SHIFT; matchptr->offset = cur_pos - match_pos; matchptr++; } interval_idx = next_interval_idx; } return matchptr - matches; } static forceinline u64 get_pos_data_size(size_t max_bufsize) { return (u64)max((u64)max_bufsize + PREFETCH_SAFETY, DIVSUFSORT_TMP_LEN) * sizeof(u32); } static forceinline u64 get_intervals_size(size_t max_bufsize) { return ((u64)max_bufsize + PREFETCH_SAFETY) * (max_bufsize <= MAX_NORMAL_BUFSIZE ? sizeof(u32) : sizeof(u64)); } /* * Calculate the number of bytes of memory needed for the LCP-interval tree * matchfinder. * * @max_bufsize - maximum buffer size to support * * Returns the number of bytes required. */ u64 lcpit_matchfinder_get_needed_memory(size_t max_bufsize) { return get_pos_data_size(max_bufsize) + get_intervals_size(max_bufsize); } /* * Initialize the LCP-interval tree matchfinder. * * @mf - the matchfinder structure to initialize * @max_bufsize - maximum buffer size to support * @min_match_len - minimum match length in bytes * @nice_match_len - only consider this many bytes of each match * * Returns true if successfully initialized; false if out of memory. */ bool lcpit_matchfinder_init(struct lcpit_matchfinder *mf, size_t max_bufsize, u32 min_match_len, u32 nice_match_len) { if (lcpit_matchfinder_get_needed_memory(max_bufsize) > SIZE_MAX) return false; if (max_bufsize > MAX_HUGE_BUFSIZE - PREFETCH_SAFETY) return false; mf->pos_data = MALLOC(get_pos_data_size(max_bufsize)); mf->intervals = MALLOC(get_intervals_size(max_bufsize)); if (!mf->pos_data || !mf->intervals) { lcpit_matchfinder_destroy(mf); return false; } mf->min_match_len = min_match_len; mf->orig_nice_match_len = nice_match_len; return true; } /* * Build the suffix array SA for the specified byte array T of length n. * * The suffix array is a sorted array of the byte array's suffixes, represented * by indices into the byte array. It can equivalently be viewed as a mapping * from suffix rank to suffix position. * * To build the suffix array, we use libdivsufsort, which uses an * induced-sorting-based algorithm. In practice, this seems to be the fastest * suffix array construction algorithm currently available. * * References: * * Y. Mori. libdivsufsort, a lightweight suffix-sorting library. * https://github.com/y-256/libdivsufsort * * G. Nong, S. Zhang, and W.H. Chan. 2009. Linear Suffix Array * Construction by Almost Pure Induced-Sorting. Data Compression * Conference, 2009. DCC '09. pp. 193 - 202. * * S.J. Puglisi, W.F. Smyth, and A. Turpin. 2007. A Taxonomy of Suffix * Array Construction Algorithms. ACM Computing Surveys (CSUR) Volume 39 * Issue 2, 2007 Article No. 4. */ static void build_SA(u32 SA[], const u8 T[], u32 n, u32 *tmp) { /* Note: divsufsort() requires a fixed amount of temporary space. The * implementation of divsufsort() has been modified from the original to * use the provided temporary space instead of allocating its own, since * we don't want to have to deal with malloc() failures here. */ divsufsort(T, SA, n, tmp); } /* * Build the inverse suffix array ISA from the suffix array SA. * * Whereas the suffix array is a mapping from suffix rank to suffix position, * the inverse suffix array is a mapping from suffix position to suffix rank. */ static void build_ISA(u32 ISA[restrict], const u32 SA[restrict], u32 n) { for (u32 r = 0; r < n; r++) ISA[SA[r]] = r; } /* * Prepare the LCP-interval tree matchfinder for a new input buffer. * * @mf - the initialized matchfinder structure * @T - the input buffer * @n - size of the input buffer in bytes. This must be nonzero and can be at * most the max_bufsize with which lcpit_matchfinder_init() was called. */ void lcpit_matchfinder_load_buffer(struct lcpit_matchfinder *mf, const u8 *T, u32 n) { /* intervals[] temporarily stores SA and LCP packed together. * pos_data[] temporarily stores ISA. * pos_data[] is also used as the temporary space for divsufsort(). */ build_SA(mf->intervals, T, n, mf->pos_data); build_ISA(mf->pos_data, mf->intervals, n); if (n <= MAX_NORMAL_BUFSIZE) { mf->nice_match_len = min(mf->orig_nice_match_len, LCP_MAX); for (u32 i = 0; i < PREFETCH_SAFETY; i++) { mf->intervals[n + i] = 0; mf->pos_data[n + i] = 0; } build_LCP(mf->intervals, mf->pos_data, T, n, mf->min_match_len, mf->nice_match_len); build_LCPIT(mf->intervals, mf->pos_data, n); mf->huge_mode = false; } else { mf->nice_match_len = min(mf->orig_nice_match_len, HUGE_LCP_MAX); for (u32 i = 0; i < PREFETCH_SAFETY; i++) { mf->intervals64[n + i] = 0; mf->pos_data[n + i] = 0; } expand_SA(mf->intervals, n); build_LCP_huge(mf->intervals64, mf->pos_data, T, n, mf->min_match_len, mf->nice_match_len); build_LCPIT_huge(mf->intervals64, mf->pos_data, n); mf->huge_mode = true; } mf->cur_pos = 0; /* starting at beginning of input buffer */ for (u32 i = 0; i < ARRAY_LEN(mf->next); i++) mf->next[i] = 0; } /* * Retrieve a list of matches with the next position. * * The matches will be recorded in the @matches array, ordered by strictly * decreasing length and strictly decreasing offset. * * The return value is the number of matches found and written to @matches. * This can be any value in [0, nice_match_len - min_match_len + 1]. */ u32 lcpit_matchfinder_get_matches(struct lcpit_matchfinder *mf, struct lz_match *matches) { if (mf->huge_mode) return lcpit_advance_one_byte_huge(mf->cur_pos++, mf->pos_data, mf->intervals64, mf->next, matches, true); else return lcpit_advance_one_byte(mf->cur_pos++, mf->pos_data, mf->intervals, mf->next, matches, true); } /* * Skip the next @count bytes (don't search for matches at them). @count is * assumed to be > 0. */ void lcpit_matchfinder_skip_bytes(struct lcpit_matchfinder *mf, u32 count) { if (mf->huge_mode) { do { lcpit_advance_one_byte_huge(mf->cur_pos++, mf->pos_data, mf->intervals64, mf->next, NULL, false); } while (--count); } else { do { lcpit_advance_one_byte(mf->cur_pos++, mf->pos_data, mf->intervals, mf->next, NULL, false); } while (--count); } } /* * Destroy an LCP-interval tree matchfinder that was previously initialized with * lcpit_matchfinder_init(). */ void lcpit_matchfinder_destroy(struct lcpit_matchfinder *mf) { FREE(mf->pos_data); FREE(mf->intervals); } wimlib-1.13.1/src/divsufsort.c0000644000175000017500000013241213160354224013147 00000000000000/* * divsufsort.c for libdivsufsort-lite * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib/divsufsort.h" #include "wimlib/util.h" #define DIVSUFSORT_ASSERT(expr) /*- Constants -*/ #define ALPHABET_SIZE 256 #define BUCKET_A_SIZE (ALPHABET_SIZE) #define BUCKET_B_SIZE (ALPHABET_SIZE * ALPHABET_SIZE) #define SS_INSERTIONSORT_THRESHOLD 8 #define SS_BLOCKSIZE 1024 /* minstacksize = log(SS_BLOCKSIZE) / log(3) * 2 */ #if SS_BLOCKSIZE == 0 # define SS_MISORT_STACKSIZE (96) #elif SS_BLOCKSIZE <= 4096 # define SS_MISORT_STACKSIZE (16) #else # define SS_MISORT_STACKSIZE (24) #endif #define SS_SMERGE_STACKSIZE (32) #define TR_INSERTIONSORT_THRESHOLD (8) #define TR_STACKSIZE (64) /*- Macros -*/ #define SWAP swap #define MIN min #define MAX max #define STACK_PUSH(_a, _b, _c, _d)\ do {\ DIVSUFSORT_ASSERT(ssize < STACK_SIZE);\ stack[ssize].a = (_a), stack[ssize].b = (_b),\ stack[ssize].c = (_c), stack[ssize++].d = (_d);\ } while(0) #define STACK_PUSH5(_a, _b, _c, _d, _e)\ do {\ DIVSUFSORT_ASSERT(ssize < STACK_SIZE);\ stack[ssize].a = (_a), stack[ssize].b = (_b),\ stack[ssize].c = (_c), stack[ssize].d = (_d), stack[ssize++].e = (_e);\ } while(0) #define STACK_POP(_a, _b, _c, _d)\ do {\ DIVSUFSORT_ASSERT(0 <= ssize);\ if(ssize == 0) { return; }\ (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\ (_c) = stack[ssize].c, (_d) = stack[ssize].d;\ } while(0) #define STACK_POP5(_a, _b, _c, _d, _e)\ do {\ DIVSUFSORT_ASSERT(0 <= ssize);\ if(ssize == 0) { return; }\ (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\ (_c) = stack[ssize].c, (_d) = stack[ssize].d, (_e) = stack[ssize].e;\ } while(0) #define BUCKET_A(_c0) bucket_A[(_c0)] #if ALPHABET_SIZE == 256 #define BUCKET_B(_c0, _c1) (bucket_B[((_c1) << 8) | (_c0)]) #define BUCKET_BSTAR(_c0, _c1) (bucket_B[((_c0) << 8) | (_c1)]) #else #define BUCKET_B(_c0, _c1) (bucket_B[(_c1) * ALPHABET_SIZE + (_c0)]) #define BUCKET_BSTAR(_c0, _c1) (bucket_B[(_c0) * ALPHABET_SIZE + (_c1)]) #endif /*- Private Functions -*/ static const int lg_table[256]= { -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 }; #if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) static forceinline int ss_ilg(int n) { #if SS_BLOCKSIZE == 0 return (n & 0xffff0000) ? ((n & 0xff000000) ? 24 + lg_table[(n >> 24) & 0xff] : 16 + lg_table[(n >> 16) & 0xff]) : ((n & 0x0000ff00) ? 8 + lg_table[(n >> 8) & 0xff] : 0 + lg_table[(n >> 0) & 0xff]); #elif SS_BLOCKSIZE < 256 return lg_table[n]; #else return (n & 0xff00) ? 8 + lg_table[(n >> 8) & 0xff] : 0 + lg_table[(n >> 0) & 0xff]; #endif } #endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */ #if SS_BLOCKSIZE != 0 static const int sqq_table[256] = { 0, 16, 22, 27, 32, 35, 39, 42, 45, 48, 50, 53, 55, 57, 59, 61, 64, 65, 67, 69, 71, 73, 75, 76, 78, 80, 81, 83, 84, 86, 87, 89, 90, 91, 93, 94, 96, 97, 98, 99, 101, 102, 103, 104, 106, 107, 108, 109, 110, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 128, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 144, 145, 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155, 156, 157, 158, 159, 160, 160, 161, 162, 163, 163, 164, 165, 166, 167, 167, 168, 169, 170, 170, 171, 172, 173, 173, 174, 175, 176, 176, 177, 178, 178, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, 192, 192, 193, 193, 194, 195, 195, 196, 197, 197, 198, 199, 199, 200, 201, 201, 202, 203, 203, 204, 204, 205, 206, 206, 207, 208, 208, 209, 209, 210, 211, 211, 212, 212, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 221, 221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227, 227, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, 253, 254, 254, 255 }; static forceinline int ss_isqrt(int x) { int y, e; if(x >= (SS_BLOCKSIZE * SS_BLOCKSIZE)) { return SS_BLOCKSIZE; } e = (x & 0xffff0000) ? ((x & 0xff000000) ? 24 + lg_table[(x >> 24) & 0xff] : 16 + lg_table[(x >> 16) & 0xff]) : ((x & 0x0000ff00) ? 8 + lg_table[(x >> 8) & 0xff] : 0 + lg_table[(x >> 0) & 0xff]); if(e >= 16) { y = sqq_table[x >> ((e - 6) - (e & 1))] << ((e >> 1) - 7); if(e >= 24) { y = (y + 1 + x / y) >> 1; } y = (y + 1 + x / y) >> 1; } else if(e >= 8) { y = (sqq_table[x >> ((e - 6) - (e & 1))] >> (7 - (e >> 1))) + 1; } else { return sqq_table[x] >> 4; } return (x < (y * y)) ? y - 1 : y; } #endif /* SS_BLOCKSIZE != 0 */ /*---------------------------------------------------------------------------*/ /* Compares two suffixes. */ static forceinline int ss_compare(const unsigned char *T, const int *p1, const int *p2, int depth) { const unsigned char *U1, *U2, *U1n, *U2n; for(U1 = T + depth + *p1, U2 = T + depth + *p2, U1n = T + *(p1 + 1) + 2, U2n = T + *(p2 + 1) + 2; (U1 < U1n) && (U2 < U2n) && (*U1 == *U2); ++U1, ++U2) { } return U1 < U1n ? (U2 < U2n ? *U1 - *U2 : 1) : (U2 < U2n ? -1 : 0); } /*---------------------------------------------------------------------------*/ #if (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1) /* Insertionsort for small size groups */ static void ss_insertionsort(const unsigned char *T, const int *PA, int *first, int *last, int depth) { int *i, *j; int t; int r; for(i = last - 2; first <= i; --i) { for(t = *i, j = i + 1; 0 < (r = ss_compare(T, PA + t, PA + *j, depth));) { do { *(j - 1) = *j; } while((++j < last) && (*j < 0)); if(last <= j) { break; } } if(r == 0) { *j = ~*j; } *(j - 1) = t; } } #endif /* (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1) */ /*---------------------------------------------------------------------------*/ #if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) static forceinline void ss_fixdown(const unsigned char *Td, const int *PA, int *SA, int i, int size) { int j, k; int v; int c, d, e; for(v = SA[i], c = Td[PA[v]]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) { d = Td[PA[SA[k = j++]]]; if(d < (e = Td[PA[SA[j]]])) { k = j; d = e; } if(d <= c) { break; } } SA[i] = v; } /* Simple top-down heapsort. */ static void ss_heapsort(const unsigned char *Td, const int *PA, int *SA, int size) { int i, m; int t; m = size; if((size % 2) == 0) { m--; if(Td[PA[SA[m / 2]]] < Td[PA[SA[m]]]) { SWAP(SA[m], SA[m / 2]); } } for(i = m / 2 - 1; 0 <= i; --i) { ss_fixdown(Td, PA, SA, i, m); } if((size % 2) == 0) { SWAP(SA[0], SA[m]); ss_fixdown(Td, PA, SA, 0, m); } for(i = m - 1; 0 < i; --i) { t = SA[0], SA[0] = SA[i]; ss_fixdown(Td, PA, SA, 0, i); SA[i] = t; } } /*---------------------------------------------------------------------------*/ /* Returns the median of three elements. */ static forceinline int * ss_median3(const unsigned char *Td, const int *PA, int *v1, int *v2, int *v3) { if(Td[PA[*v1]] > Td[PA[*v2]]) { SWAP(v1, v2); } if(Td[PA[*v2]] > Td[PA[*v3]]) { if(Td[PA[*v1]] > Td[PA[*v3]]) { return v1; } else { return v3; } } return v2; } /* Returns the median of five elements. */ static forceinline int * ss_median5(const unsigned char *Td, const int *PA, int *v1, int *v2, int *v3, int *v4, int *v5) { if(Td[PA[*v2]] > Td[PA[*v3]]) { SWAP(v2, v3); } if(Td[PA[*v4]] > Td[PA[*v5]]) { SWAP(v4, v5); } if(Td[PA[*v2]] > Td[PA[*v4]]) { SWAP(v2, v4); SWAP(v3, v5); } if(Td[PA[*v1]] > Td[PA[*v3]]) { SWAP(v1, v3); } if(Td[PA[*v1]] > Td[PA[*v4]]) { SWAP(v1, v4); SWAP(v3, v5); } if(Td[PA[*v3]] > Td[PA[*v4]]) { return v4; } return v3; } /* Returns the pivot element. */ static forceinline int * ss_pivot(const unsigned char *Td, const int *PA, int *first, int *last) { int *middle; int t; t = last - first; middle = first + t / 2; if(t <= 512) { if(t <= 32) { return ss_median3(Td, PA, first, middle, last - 1); } else { t >>= 2; return ss_median5(Td, PA, first, first + t, middle, last - 1 - t, last - 1); } } t >>= 3; first = ss_median3(Td, PA, first, first + t, first + (t << 1)); middle = ss_median3(Td, PA, middle - t, middle, middle + t); last = ss_median3(Td, PA, last - 1 - (t << 1), last - 1 - t, last - 1); return ss_median3(Td, PA, first, middle, last); } /*---------------------------------------------------------------------------*/ /* Binary partition for substrings. */ static forceinline int * ss_partition(const int *PA, int *first, int *last, int depth) { int *a, *b; int t; for(a = first - 1, b = last;;) { for(; (++a < b) && ((PA[*a] + depth) >= (PA[*a + 1] + 1));) { *a = ~*a; } for(; (a < --b) && ((PA[*b] + depth) < (PA[*b + 1] + 1));) { } if(b <= a) { break; } t = ~*b; *b = *a; *a = t; } if(first < a) { *first = ~*first; } return a; } /* Multikey introsort for medium size groups. */ static void ss_mintrosort(const unsigned char *T, const int *PA, int *first, int *last, int depth) { #define STACK_SIZE SS_MISORT_STACKSIZE struct { int *a, *b, c; int d; } stack[STACK_SIZE]; const unsigned char *Td; int *a, *b, *c, *d, *e, *f; int s, t; int ssize; int limit; int v, x = 0; for(ssize = 0, limit = ss_ilg(last - first);;) { if((last - first) <= SS_INSERTIONSORT_THRESHOLD) { #if 1 < SS_INSERTIONSORT_THRESHOLD if(1 < (last - first)) { ss_insertionsort(T, PA, first, last, depth); } #endif STACK_POP(first, last, depth, limit); continue; } Td = T + depth; if(limit-- == 0) { ss_heapsort(Td, PA, first, last - first); } if(limit < 0) { for(a = first + 1, v = Td[PA[*first]]; a < last; ++a) { if((x = Td[PA[*a]]) != v) { if(1 < (a - first)) { break; } v = x; first = a; } } if(Td[PA[*first] - 1] < v) { first = ss_partition(PA, first, a, depth); } if((a - first) <= (last - a)) { if(1 < (a - first)) { STACK_PUSH(a, last, depth, -1); last = a, depth += 1, limit = ss_ilg(a - first); } else { first = a, limit = -1; } } else { if(1 < (last - a)) { STACK_PUSH(first, a, depth + 1, ss_ilg(a - first)); first = a, limit = -1; } else { last = a, depth += 1, limit = ss_ilg(a - first); } } continue; } /* choose pivot */ a = ss_pivot(Td, PA, first, last); v = Td[PA[*a]]; SWAP(*first, *a); /* partition */ for(b = first; (++b < last) && ((x = Td[PA[*b]]) == v);) { } if(((a = b) < last) && (x < v)) { for(; (++b < last) && ((x = Td[PA[*b]]) <= v);) { if(x == v) { SWAP(*b, *a); ++a; } } } for(c = last; (b < --c) && ((x = Td[PA[*c]]) == v);) { } if((b < (d = c)) && (x > v)) { for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) { if(x == v) { SWAP(*c, *d); --d; } } } for(; b < c;) { SWAP(*b, *c); for(; (++b < c) && ((x = Td[PA[*b]]) <= v);) { if(x == v) { SWAP(*b, *a); ++a; } } for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) { if(x == v) { SWAP(*c, *d); --d; } } } if(a <= d) { c = b - 1; if((s = a - first) > (t = b - a)) { s = t; } for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } if((s = d - c) > (t = last - d - 1)) { s = t; } for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } a = first + (b - a), c = last - (d - c); b = (v <= Td[PA[*a] - 1]) ? a : ss_partition(PA, a, c, depth); if((a - first) <= (last - c)) { if((last - c) <= (c - b)) { STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); STACK_PUSH(c, last, depth, limit); last = a; } else if((a - first) <= (c - b)) { STACK_PUSH(c, last, depth, limit); STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); last = a; } else { STACK_PUSH(c, last, depth, limit); STACK_PUSH(first, a, depth, limit); first = b, last = c, depth += 1, limit = ss_ilg(c - b); } } else { if((a - first) <= (c - b)) { STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); STACK_PUSH(first, a, depth, limit); first = c; } else if((last - c) <= (c - b)) { STACK_PUSH(first, a, depth, limit); STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); first = c; } else { STACK_PUSH(first, a, depth, limit); STACK_PUSH(c, last, depth, limit); first = b, last = c, depth += 1, limit = ss_ilg(c - b); } } } else { limit += 1; if(Td[PA[*first] - 1] < v) { first = ss_partition(PA, first, last, depth); limit = ss_ilg(last - first); } depth += 1; } } #undef STACK_SIZE } #endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */ /*---------------------------------------------------------------------------*/ #if SS_BLOCKSIZE != 0 static forceinline void ss_blockswap(int *a, int *b, int n) { int t; for(; 0 < n; --n, ++a, ++b) { t = *a, *a = *b, *b = t; } } static forceinline void ss_rotate(int *first, int *middle, int *last) { int *a, *b, t; int l, r; l = middle - first, r = last - middle; for(; (0 < l) && (0 < r);) { if(l == r) { ss_blockswap(first, middle, l); break; } if(l < r) { a = last - 1, b = middle - 1; t = *a; do { *a-- = *b, *b-- = *a; if(b < first) { *a = t; last = a; if((r -= l + 1) <= l) { break; } a -= 1, b = middle - 1; t = *a; } } while(1); } else { a = first, b = middle; t = *a; do { *a++ = *b, *b++ = *a; if(last <= b) { *a = t; first = a + 1; if((l -= r + 1) <= r) { break; } a += 1, b = middle; t = *a; } } while(1); } } } /*---------------------------------------------------------------------------*/ static void ss_inplacemerge(const unsigned char *T, const int *PA, int *first, int *middle, int *last, int depth) { const int *p; int *a, *b; int len, half; int q, r; int x; for(;;) { if(*(last - 1) < 0) { x = 1; p = PA + ~*(last - 1); } else { x = 0; p = PA + *(last - 1); } for(a = first, len = middle - first, half = len >> 1, r = -1; 0 < len; len = half, half >>= 1) { b = a + half; q = ss_compare(T, PA + ((0 <= *b) ? *b : ~*b), p, depth); if(q < 0) { a = b + 1; half -= (len & 1) ^ 1; } else { r = q; } } if(a < middle) { if(r == 0) { *a = ~*a; } ss_rotate(a, middle, last); last -= middle - a; middle = a; if(first == middle) { break; } } --last; if(x != 0) { while(*--last < 0) { } } if(middle == last) { break; } } } /*---------------------------------------------------------------------------*/ /* Merge-forward with internal buffer. */ static void ss_mergeforward(const unsigned char *T, const int *PA, int *first, int *middle, int *last, int *buf, int depth) { int *a, *b, *c, *bufend; int t; int r; bufend = buf + (middle - first) - 1; ss_blockswap(buf, first, middle - first); for(t = *(a = first), b = buf, c = middle;;) { r = ss_compare(T, PA + *b, PA + *c, depth); if(r < 0) { do { *a++ = *b; if(bufend <= b) { *bufend = t; return; } *b++ = *a; } while(*b < 0); } else if(r > 0) { do { *a++ = *c, *c++ = *a; if(last <= c) { while(b < bufend) { *a++ = *b, *b++ = *a; } *a = *b, *b = t; return; } } while(*c < 0); } else { *c = ~*c; do { *a++ = *b; if(bufend <= b) { *bufend = t; return; } *b++ = *a; } while(*b < 0); do { *a++ = *c, *c++ = *a; if(last <= c) { while(b < bufend) { *a++ = *b, *b++ = *a; } *a = *b, *b = t; return; } } while(*c < 0); } } } /* Merge-backward with internal buffer. */ static void ss_mergebackward(const unsigned char *T, const int *PA, int *first, int *middle, int *last, int *buf, int depth) { const int *p1, *p2; int *a, *b, *c, *bufend; int t; int r; int x; bufend = buf + (last - middle) - 1; ss_blockswap(buf, middle, last - middle); x = 0; if(*bufend < 0) { p1 = PA + ~*bufend; x |= 1; } else { p1 = PA + *bufend; } if(*(middle - 1) < 0) { p2 = PA + ~*(middle - 1); x |= 2; } else { p2 = PA + *(middle - 1); } for(t = *(a = last - 1), b = bufend, c = middle - 1;;) { r = ss_compare(T, p1, p2, depth); if(0 < r) { if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; } *a-- = *b; if(b <= buf) { *buf = t; break; } *b-- = *a; if(*b < 0) { p1 = PA + ~*b; x |= 1; } else { p1 = PA + *b; } } else if(r < 0) { if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; } *a-- = *c, *c-- = *a; if(c < first) { while(buf < b) { *a-- = *b, *b-- = *a; } *a = *b, *b = t; break; } if(*c < 0) { p2 = PA + ~*c; x |= 2; } else { p2 = PA + *c; } } else { if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; } *a-- = ~*b; if(b <= buf) { *buf = t; break; } *b-- = *a; if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; } *a-- = *c, *c-- = *a; if(c < first) { while(buf < b) { *a-- = *b, *b-- = *a; } *a = *b, *b = t; break; } if(*b < 0) { p1 = PA + ~*b; x |= 1; } else { p1 = PA + *b; } if(*c < 0) { p2 = PA + ~*c; x |= 2; } else { p2 = PA + *c; } } } } /* D&C based merge. */ static void ss_swapmerge(const unsigned char *T, const int *PA, int *first, int *middle, int *last, int *buf, int bufsize, int depth) { #define STACK_SIZE SS_SMERGE_STACKSIZE #define GETIDX(a) ((0 <= (a)) ? (a) : (~(a))) #define MERGE_CHECK(a, b, c)\ do {\ if(((c) & 1) ||\ (((c) & 2) && (ss_compare(T, PA + GETIDX(*((a) - 1)), PA + *(a), depth) == 0))) {\ *(a) = ~*(a);\ }\ if(((c) & 4) && ((ss_compare(T, PA + GETIDX(*((b) - 1)), PA + *(b), depth) == 0))) {\ *(b) = ~*(b);\ }\ } while(0) struct { int *a, *b, *c; int d; } stack[STACK_SIZE]; int *l, *r, *lm, *rm; int m, len, half; int ssize; int check, next; for(check = 0, ssize = 0;;) { if((last - middle) <= bufsize) { if((first < middle) && (middle < last)) { ss_mergebackward(T, PA, first, middle, last, buf, depth); } MERGE_CHECK(first, last, check); STACK_POP(first, middle, last, check); continue; } if((middle - first) <= bufsize) { if(first < middle) { ss_mergeforward(T, PA, first, middle, last, buf, depth); } MERGE_CHECK(first, last, check); STACK_POP(first, middle, last, check); continue; } for(m = 0, len = MIN(middle - first, last - middle), half = len >> 1; 0 < len; len = half, half >>= 1) { if(ss_compare(T, PA + GETIDX(*(middle + m + half)), PA + GETIDX(*(middle - m - half - 1)), depth) < 0) { m += half + 1; half -= (len & 1) ^ 1; } } if(0 < m) { lm = middle - m, rm = middle + m; ss_blockswap(lm, middle, m); l = r = middle, next = 0; if(rm < last) { if(*rm < 0) { *rm = ~*rm; if(first < lm) { for(; *--l < 0;) { } next |= 4; } next |= 1; } else if(first < lm) { for(; *r < 0; ++r) { } next |= 2; } } if((l - first) <= (last - r)) { STACK_PUSH(r, rm, last, (next & 3) | (check & 4)); middle = lm, last = l, check = (check & 3) | (next & 4); } else { if((next & 2) && (r == middle)) { next ^= 6; } STACK_PUSH(first, lm, l, (check & 3) | (next & 4)); first = r, middle = rm, check = (next & 3) | (check & 4); } } else { if(ss_compare(T, PA + GETIDX(*(middle - 1)), PA + *middle, depth) == 0) { *middle = ~*middle; } MERGE_CHECK(first, last, check); STACK_POP(first, middle, last, check); } } #undef STACK_SIZE } #endif /* SS_BLOCKSIZE != 0 */ /*---------------------------------------------------------------------------*/ /* Substring sort */ static void sssort(const unsigned char *T, const int *PA, int *first, int *last, int *buf, int bufsize, int depth, int n, int lastsuffix) { int *a; #if SS_BLOCKSIZE != 0 int *b, *middle, *curbuf; int j, k, curbufsize, limit; #endif int i; if(lastsuffix != 0) { ++first; } #if SS_BLOCKSIZE == 0 ss_mintrosort(T, PA, first, last, depth); #else if((bufsize < SS_BLOCKSIZE) && (bufsize < (last - first)) && (bufsize < (limit = ss_isqrt(last - first)))) { if(SS_BLOCKSIZE < limit) { limit = SS_BLOCKSIZE; } buf = middle = last - limit, bufsize = limit; } else { middle = last, limit = 0; } for(a = first, i = 0; SS_BLOCKSIZE < (middle - a); a += SS_BLOCKSIZE, ++i) { #if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE ss_mintrosort(T, PA, a, a + SS_BLOCKSIZE, depth); #elif 1 < SS_BLOCKSIZE ss_insertionsort(T, PA, a, a + SS_BLOCKSIZE, depth); #endif curbufsize = last - (a + SS_BLOCKSIZE); curbuf = a + SS_BLOCKSIZE; if(curbufsize <= bufsize) { curbufsize = bufsize, curbuf = buf; } for(b = a, k = SS_BLOCKSIZE, j = i; j & 1; b -= k, k <<= 1, j >>= 1) { ss_swapmerge(T, PA, b - k, b, b + k, curbuf, curbufsize, depth); } } #if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE ss_mintrosort(T, PA, a, middle, depth); #elif 1 < SS_BLOCKSIZE ss_insertionsort(T, PA, a, middle, depth); #endif for(k = SS_BLOCKSIZE; i != 0; k <<= 1, i >>= 1) { if(i & 1) { ss_swapmerge(T, PA, a - k, a, middle, buf, bufsize, depth); a -= k; } } if(limit != 0) { #if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE ss_mintrosort(T, PA, middle, last, depth); #elif 1 < SS_BLOCKSIZE ss_insertionsort(T, PA, middle, last, depth); #endif ss_inplacemerge(T, PA, first, middle, last, depth); } #endif if(lastsuffix != 0) { /* Insert last type B* suffix. */ int PAi[2]; PAi[0] = PA[*(first - 1)], PAi[1] = n - 2; for(a = first, i = *(first - 1); (a < last) && ((*a < 0) || (0 < ss_compare(T, &(PAi[0]), PA + *a, depth))); ++a) { *(a - 1) = *a; } *(a - 1) = i; } } /*---------------------------------------------------------------------------*/ static forceinline int tr_ilg(int n) { return (n & 0xffff0000) ? ((n & 0xff000000) ? 24 + lg_table[(n >> 24) & 0xff] : 16 + lg_table[(n >> 16) & 0xff]) : ((n & 0x0000ff00) ? 8 + lg_table[(n >> 8) & 0xff] : 0 + lg_table[(n >> 0) & 0xff]); } /*---------------------------------------------------------------------------*/ /* Simple insertionsort for small size groups. */ static void tr_insertionsort(const int *ISAd, int *first, int *last) { int *a, *b; int t, r; for(a = first + 1; a < last; ++a) { for(t = *a, b = a - 1; 0 > (r = ISAd[t] - ISAd[*b]);) { do { *(b + 1) = *b; } while((first <= --b) && (*b < 0)); if(b < first) { break; } } if(r == 0) { *b = ~*b; } *(b + 1) = t; } } /*---------------------------------------------------------------------------*/ static forceinline void tr_fixdown(const int *ISAd, int *SA, int i, int size) { int j, k; int v; int c, d, e; for(v = SA[i], c = ISAd[v]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) { d = ISAd[SA[k = j++]]; if(d < (e = ISAd[SA[j]])) { k = j; d = e; } if(d <= c) { break; } } SA[i] = v; } /* Simple top-down heapsort. */ static void tr_heapsort(const int *ISAd, int *SA, int size) { int i, m; int t; m = size; if((size % 2) == 0) { m--; if(ISAd[SA[m / 2]] < ISAd[SA[m]]) { SWAP(SA[m], SA[m / 2]); } } for(i = m / 2 - 1; 0 <= i; --i) { tr_fixdown(ISAd, SA, i, m); } if((size % 2) == 0) { SWAP(SA[0], SA[m]); tr_fixdown(ISAd, SA, 0, m); } for(i = m - 1; 0 < i; --i) { t = SA[0], SA[0] = SA[i]; tr_fixdown(ISAd, SA, 0, i); SA[i] = t; } } /*---------------------------------------------------------------------------*/ /* Returns the median of three elements. */ static forceinline int * tr_median3(const int *ISAd, int *v1, int *v2, int *v3) { if(ISAd[*v1] > ISAd[*v2]) { SWAP(v1, v2); } if(ISAd[*v2] > ISAd[*v3]) { if(ISAd[*v1] > ISAd[*v3]) { return v1; } else { return v3; } } return v2; } /* Returns the median of five elements. */ static forceinline int * tr_median5(const int *ISAd, int *v1, int *v2, int *v3, int *v4, int *v5) { if(ISAd[*v2] > ISAd[*v3]) { SWAP(v2, v3); } if(ISAd[*v4] > ISAd[*v5]) { SWAP(v4, v5); } if(ISAd[*v2] > ISAd[*v4]) { SWAP(v2, v4); SWAP(v3, v5); } if(ISAd[*v1] > ISAd[*v3]) { SWAP(v1, v3); } if(ISAd[*v1] > ISAd[*v4]) { SWAP(v1, v4); SWAP(v3, v5); } if(ISAd[*v3] > ISAd[*v4]) { return v4; } return v3; } /* Returns the pivot element. */ static forceinline int * tr_pivot(const int *ISAd, int *first, int *last) { int *middle; int t; t = last - first; middle = first + t / 2; if(t <= 512) { if(t <= 32) { return tr_median3(ISAd, first, middle, last - 1); } else { t >>= 2; return tr_median5(ISAd, first, first + t, middle, last - 1 - t, last - 1); } } t >>= 3; first = tr_median3(ISAd, first, first + t, first + (t << 1)); middle = tr_median3(ISAd, middle - t, middle, middle + t); last = tr_median3(ISAd, last - 1 - (t << 1), last - 1 - t, last - 1); return tr_median3(ISAd, first, middle, last); } /*---------------------------------------------------------------------------*/ typedef struct _trbudget_t trbudget_t; struct _trbudget_t { int chance; int remain; int incval; int count; }; static forceinline void trbudget_init(trbudget_t *budget, int chance, int incval) { budget->chance = chance; budget->remain = budget->incval = incval; } static forceinline int trbudget_check(trbudget_t *budget, int size) { if(size <= budget->remain) { budget->remain -= size; return 1; } if(budget->chance == 0) { budget->count += size; return 0; } budget->remain += budget->incval - size; budget->chance -= 1; return 1; } /*---------------------------------------------------------------------------*/ static forceinline void tr_partition(const int *ISAd, int *first, int *middle, int *last, int **pa, int **pb, int v) { int *a, *b, *c, *d, *e, *f; int t, s; int x = 0; for(b = middle - 1; (++b < last) && ((x = ISAd[*b]) == v);) { } if(((a = b) < last) && (x < v)) { for(; (++b < last) && ((x = ISAd[*b]) <= v);) { if(x == v) { SWAP(*b, *a); ++a; } } } for(c = last; (b < --c) && ((x = ISAd[*c]) == v);) { } if((b < (d = c)) && (x > v)) { for(; (b < --c) && ((x = ISAd[*c]) >= v);) { if(x == v) { SWAP(*c, *d); --d; } } } for(; b < c;) { SWAP(*b, *c); for(; (++b < c) && ((x = ISAd[*b]) <= v);) { if(x == v) { SWAP(*b, *a); ++a; } } for(; (b < --c) && ((x = ISAd[*c]) >= v);) { if(x == v) { SWAP(*c, *d); --d; } } } if(a <= d) { c = b - 1; if((s = a - first) > (t = b - a)) { s = t; } for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } if((s = d - c) > (t = last - d - 1)) { s = t; } for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } first += (b - a), last -= (d - c); } *pa = first, *pb = last; } static void tr_copy(int *ISA, const int *SA, int *first, int *a, int *b, int *last, int depth) { /* sort suffixes of middle partition by using sorted order of suffixes of left and right partition. */ int *c, *d, *e; int s, v; v = b - SA - 1; for(c = first, d = a - 1; c <= d; ++c) { if((0 <= (s = *c - depth)) && (ISA[s] == v)) { *++d = s; ISA[s] = d - SA; } } for(c = last - 1, e = d + 1, d = b; e < d; --c) { if((0 <= (s = *c - depth)) && (ISA[s] == v)) { *--d = s; ISA[s] = d - SA; } } } static void tr_partialcopy(int *ISA, const int *SA, int *first, int *a, int *b, int *last, int depth) { int *c, *d, *e; int s, v; int rank, lastrank, newrank = -1; v = b - SA - 1; lastrank = -1; for(c = first, d = a - 1; c <= d; ++c) { if((0 <= (s = *c - depth)) && (ISA[s] == v)) { *++d = s; rank = ISA[s + depth]; if(lastrank != rank) { lastrank = rank; newrank = d - SA; } ISA[s] = newrank; } } lastrank = -1; for(e = d; first <= e; --e) { rank = ISA[*e]; if(lastrank != rank) { lastrank = rank; newrank = e - SA; } if(newrank != rank) { ISA[*e] = newrank; } } lastrank = -1; for(c = last - 1, e = d + 1, d = b; e < d; --c) { if((0 <= (s = *c - depth)) && (ISA[s] == v)) { *--d = s; rank = ISA[s + depth]; if(lastrank != rank) { lastrank = rank; newrank = d - SA; } ISA[s] = newrank; } } } static void tr_introsort(int *ISA, const int *ISAd, int *SA, int *first, int *last, trbudget_t *budget) { #define STACK_SIZE TR_STACKSIZE struct { const int *a; int *b, *c; int d, e; }stack[STACK_SIZE]; int *a, *b, *c; int v, x = 0; int incr = ISAd - ISA; int limit, next; int ssize, trlink = -1; for(ssize = 0, limit = tr_ilg(last - first);;) { if(limit < 0) { if(limit == -1) { /* tandem repeat partition */ tr_partition(ISAd - incr, first, first, last, &a, &b, last - SA - 1); /* update ranks */ if(a < last) { for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; } } if(b < last) { for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } } /* push */ if(1 < (b - a)) { STACK_PUSH5(NULL, a, b, 0, 0); STACK_PUSH5(ISAd - incr, first, last, -2, trlink); trlink = ssize - 2; } if((a - first) <= (last - b)) { if(1 < (a - first)) { STACK_PUSH5(ISAd, b, last, tr_ilg(last - b), trlink); last = a, limit = tr_ilg(a - first); } else if(1 < (last - b)) { first = b, limit = tr_ilg(last - b); } else { STACK_POP5(ISAd, first, last, limit, trlink); } } else { if(1 < (last - b)) { STACK_PUSH5(ISAd, first, a, tr_ilg(a - first), trlink); first = b, limit = tr_ilg(last - b); } else if(1 < (a - first)) { last = a, limit = tr_ilg(a - first); } else { STACK_POP5(ISAd, first, last, limit, trlink); } } } else if(limit == -2) { /* tandem repeat copy */ a = stack[--ssize].b, b = stack[ssize].c; if(stack[ssize].d == 0) { tr_copy(ISA, SA, first, a, b, last, ISAd - ISA); } else { if(0 <= trlink) { stack[trlink].d = -1; } tr_partialcopy(ISA, SA, first, a, b, last, ISAd - ISA); } STACK_POP5(ISAd, first, last, limit, trlink); } else { /* sorted partition */ if(0 <= *first) { a = first; do { ISA[*a] = a - SA; } while((++a < last) && (0 <= *a)); first = a; } if(first < last) { a = first; do { *a = ~*a; } while(*++a < 0); next = (ISA[*a] != ISAd[*a]) ? tr_ilg(a - first + 1) : -1; if(++a < last) { for(b = first, v = a - SA - 1; b < a; ++b) { ISA[*b] = v; } } /* push */ if(trbudget_check(budget, a - first)) { if((a - first) <= (last - a)) { STACK_PUSH5(ISAd, a, last, -3, trlink); ISAd += incr, last = a, limit = next; } else { if(1 < (last - a)) { STACK_PUSH5(ISAd + incr, first, a, next, trlink); first = a, limit = -3; } else { ISAd += incr, last = a, limit = next; } } } else { if(0 <= trlink) { stack[trlink].d = -1; } if(1 < (last - a)) { first = a, limit = -3; } else { STACK_POP5(ISAd, first, last, limit, trlink); } } } else { STACK_POP5(ISAd, first, last, limit, trlink); } } continue; } if((last - first) <= TR_INSERTIONSORT_THRESHOLD) { tr_insertionsort(ISAd, first, last); limit = -3; continue; } if(limit-- == 0) { tr_heapsort(ISAd, first, last - first); for(a = last - 1; first < a; a = b) { for(x = ISAd[*a], b = a - 1; (first <= b) && (ISAd[*b] == x); --b) { *b = ~*b; } } limit = -3; continue; } /* choose pivot */ a = tr_pivot(ISAd, first, last); SWAP(*first, *a); v = ISAd[*first]; /* partition */ tr_partition(ISAd, first, first + 1, last, &a, &b, v); if((last - first) != (b - a)) { next = (ISA[*a] != v) ? tr_ilg(b - a) : -1; /* update ranks */ for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; } if(b < last) { for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } } /* push */ if((1 < (b - a)) && (trbudget_check(budget, b - a))) { if((a - first) <= (last - b)) { if((last - b) <= (b - a)) { if(1 < (a - first)) { STACK_PUSH5(ISAd + incr, a, b, next, trlink); STACK_PUSH5(ISAd, b, last, limit, trlink); last = a; } else if(1 < (last - b)) { STACK_PUSH5(ISAd + incr, a, b, next, trlink); first = b; } else { ISAd += incr, first = a, last = b, limit = next; } } else if((a - first) <= (b - a)) { if(1 < (a - first)) { STACK_PUSH5(ISAd, b, last, limit, trlink); STACK_PUSH5(ISAd + incr, a, b, next, trlink); last = a; } else { STACK_PUSH5(ISAd, b, last, limit, trlink); ISAd += incr, first = a, last = b, limit = next; } } else { STACK_PUSH5(ISAd, b, last, limit, trlink); STACK_PUSH5(ISAd, first, a, limit, trlink); ISAd += incr, first = a, last = b, limit = next; } } else { if((a - first) <= (b - a)) { if(1 < (last - b)) { STACK_PUSH5(ISAd + incr, a, b, next, trlink); STACK_PUSH5(ISAd, first, a, limit, trlink); first = b; } else if(1 < (a - first)) { STACK_PUSH5(ISAd + incr, a, b, next, trlink); last = a; } else { ISAd += incr, first = a, last = b, limit = next; } } else if((last - b) <= (b - a)) { if(1 < (last - b)) { STACK_PUSH5(ISAd, first, a, limit, trlink); STACK_PUSH5(ISAd + incr, a, b, next, trlink); first = b; } else { STACK_PUSH5(ISAd, first, a, limit, trlink); ISAd += incr, first = a, last = b, limit = next; } } else { STACK_PUSH5(ISAd, first, a, limit, trlink); STACK_PUSH5(ISAd, b, last, limit, trlink); ISAd += incr, first = a, last = b, limit = next; } } } else { if((1 < (b - a)) && (0 <= trlink)) { stack[trlink].d = -1; } if((a - first) <= (last - b)) { if(1 < (a - first)) { STACK_PUSH5(ISAd, b, last, limit, trlink); last = a; } else if(1 < (last - b)) { first = b; } else { STACK_POP5(ISAd, first, last, limit, trlink); } } else { if(1 < (last - b)) { STACK_PUSH5(ISAd, first, a, limit, trlink); first = b; } else if(1 < (a - first)) { last = a; } else { STACK_POP5(ISAd, first, last, limit, trlink); } } } } else { if(trbudget_check(budget, last - first)) { limit = tr_ilg(last - first), ISAd += incr; } else { if(0 <= trlink) { stack[trlink].d = -1; } STACK_POP5(ISAd, first, last, limit, trlink); } } } #undef STACK_SIZE } /*---------------------------------------------------------------------------*/ /* Tandem repeat sort */ static void trsort(int *ISA, int *SA, int n, int depth) { int *ISAd; int *first, *last; trbudget_t budget; int t, skip, unsorted; trbudget_init(&budget, tr_ilg(n) * 2 / 3, n); /* trbudget_init(&budget, tr_ilg(n) * 3 / 4, n); */ for(ISAd = ISA + depth; -n < *SA; ISAd += ISAd - ISA) { first = SA; skip = 0; unsorted = 0; do { if((t = *first) < 0) { first -= t; skip += t; } else { if(skip != 0) { *(first + skip) = skip; skip = 0; } last = SA + ISA[t] + 1; if(1 < (last - first)) { budget.count = 0; tr_introsort(ISA, ISAd, SA, first, last, &budget); if(budget.count != 0) { unsorted += budget.count; } else { skip = first - last; } } else if((last - first) == 1) { skip = -1; } first = last; } } while(first < (SA + n)); if(skip != 0) { *(first + skip) = skip; } if(unsorted == 0) { break; } } } /*---------------------------------------------------------------------------*/ /* Sorts suffixes of type B*. */ static int sort_typeBstar(const unsigned char *T, int *SA, int *bucket_A, int *bucket_B, int n) { int *PAb, *ISAb, *buf; int i, j, k, t, m, bufsize; int c0, c1; /* Initialize bucket arrays. */ for(i = 0; i < BUCKET_A_SIZE; ++i) { bucket_A[i] = 0; } for(i = 0; i < BUCKET_B_SIZE; ++i) { bucket_B[i] = 0; } /* Count the number of occurrences of the first one or two characters of each type A, B and B* suffix. Moreover, store the beginning position of all type B* suffixes into the array SA. */ for(i = n - 1, m = n, c0 = T[n - 1]; 0 <= i;) { /* type A suffix. */ do { ++BUCKET_A(c1 = c0); } while((0 <= --i) && ((c0 = T[i]) >= c1)); if(0 <= i) { /* type B* suffix. */ ++BUCKET_BSTAR(c0, c1); SA[--m] = i; /* type B suffix. */ for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) { ++BUCKET_B(c0, c1); } } } m = n - m; /* note: A type B* suffix is lexicographically smaller than a type B suffix that begins with the same first two characters. */ /* Calculate the index of start/end point of each bucket. */ for(c0 = 0, i = 0, j = 0; c0 < ALPHABET_SIZE; ++c0) { t = i + BUCKET_A(c0); BUCKET_A(c0) = i + j; /* start point */ i = t + BUCKET_B(c0, c0); for(c1 = c0 + 1; c1 < ALPHABET_SIZE; ++c1) { j += BUCKET_BSTAR(c0, c1); BUCKET_BSTAR(c0, c1) = j; /* end point */ i += BUCKET_B(c0, c1); } } if(0 < m) { /* Sort the type B* suffixes by their first two characters. */ PAb = SA + n - m; ISAb = SA + m; for(i = m - 2; 0 <= i; --i) { t = PAb[i], c0 = T[t], c1 = T[t + 1]; SA[--BUCKET_BSTAR(c0, c1)] = i; } t = PAb[m - 1], c0 = T[t], c1 = T[t + 1]; SA[--BUCKET_BSTAR(c0, c1)] = m - 1; /* Sort the type B* substrings using sssort. */ buf = SA + m, bufsize = n - (2 * m); for(c0 = ALPHABET_SIZE - 2, j = m; 0 < j; --c0) { for(c1 = ALPHABET_SIZE - 1; c0 < c1; j = i, --c1) { i = BUCKET_BSTAR(c0, c1); if(1 < (j - i)) { sssort(T, PAb, SA + i, SA + j, buf, bufsize, 2, n, *(SA + i) == (m - 1)); } } } /* Compute ranks of type B* substrings. */ for(i = m - 1; 0 <= i; --i) { if(0 <= SA[i]) { j = i; do { ISAb[SA[i]] = i; } while((0 <= --i) && (0 <= SA[i])); SA[i + 1] = i - j; if(i <= 0) { break; } } j = i; do { ISAb[SA[i] = ~SA[i]] = j; } while(SA[--i] < 0); ISAb[SA[i]] = j; } /* Construct the inverse suffix array of type B* suffixes using trsort. */ trsort(ISAb, SA, m, 1); /* Set the sorted order of tyoe B* suffixes. */ for(i = n - 1, j = m, c0 = T[n - 1]; 0 <= i;) { for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) >= c1); --i, c1 = c0) { } if(0 <= i) { t = i; for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) { } SA[ISAb[--j]] = ((t == 0) || (1 < (t - i))) ? t : ~t; } } /* Calculate the index of start/end point of each bucket. */ BUCKET_B(ALPHABET_SIZE - 1, ALPHABET_SIZE - 1) = n; /* end point */ for(c0 = ALPHABET_SIZE - 2, k = m - 1; 0 <= c0; --c0) { i = BUCKET_A(c0 + 1) - 1; for(c1 = ALPHABET_SIZE - 1; c0 < c1; --c1) { t = i - BUCKET_B(c0, c1); BUCKET_B(c0, c1) = i; /* end point */ /* Move all type B* suffixes to the correct position. */ for(i = t, j = BUCKET_BSTAR(c0, c1); j <= k; --i, --k) { SA[i] = SA[k]; } } BUCKET_BSTAR(c0, c0 + 1) = i - BUCKET_B(c0, c0) + 1; /* start point */ BUCKET_B(c0, c0) = i; /* end point */ } } return m; } /* Constructs the suffix array by using the sorted order of type B* suffixes. */ static void construct_SA(const unsigned char *T, int *SA, int *bucket_A, int *bucket_B, int n, int m) { int *i, *j, *k; int s; int c0, c1, c2; if(0 < m) { /* Construct the sorted order of type B suffixes by using the sorted order of type B* suffixes. */ for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) { /* Scan the suffix array from right to left. */ for(i = SA + BUCKET_BSTAR(c1, c1 + 1), j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1; i <= j; --j) { if(0 < (s = *j)) { DIVSUFSORT_ASSERT(T[s] == c1); DIVSUFSORT_ASSERT(((s + 1) < n) && (T[s] <= T[s + 1])); DIVSUFSORT_ASSERT(T[s - 1] <= T[s]); *j = ~s; c0 = T[--s]; if((0 < s) && (T[s - 1] > c0)) { s = ~s; } if(c0 != c2) { if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } k = SA + BUCKET_B(c2 = c0, c1); } DIVSUFSORT_ASSERT(k < j); *k-- = s; } else { DIVSUFSORT_ASSERT(((s == 0) && (T[s] == c1)) || (s < 0)); *j = ~s; } } } } /* Construct the suffix array by using the sorted order of type B suffixes. */ k = SA + BUCKET_A(c2 = T[n - 1]); *k++ = (T[n - 2] < c2) ? ~(n - 1) : (n - 1); /* Scan the suffix array from left to right. */ for(i = SA, j = SA + n; i < j; ++i) { if(0 < (s = *i)) { DIVSUFSORT_ASSERT(T[s - 1] >= T[s]); c0 = T[--s]; if((s == 0) || (T[s - 1] < c0)) { s = ~s; } if(c0 != c2) { BUCKET_A(c2) = k - SA; k = SA + BUCKET_A(c2 = c0); } DIVSUFSORT_ASSERT(i < k); *k++ = s; } else { DIVSUFSORT_ASSERT(s < 0); *i = ~s; } } } /*---------------------------------------------------------------------------*/ /*- Function -*/ /* XXX Modified from original: use provided temporary space instead of * allocating it. */ void divsufsort(const u8 *T, u32 *SA, u32 n, u32 *tmp) { u32 *bucket_A = tmp; u32 *bucket_B = tmp + BUCKET_A_SIZE; u32 m; switch (n) { case 0: break; case 1: SA[0] = 0; break; case 2: m = (T[0] < T[1]); SA[m ^ 1] = 0; SA[m] = 1; break; default: m = sort_typeBstar(T, SA, bucket_A, bucket_B, n); construct_SA(T, SA, bucket_A, bucket_B, n, m); break; } } wimlib-1.13.1/src/pattern.c0000644000175000017500000001320413324661664012424 00000000000000/* * pattern.c * * Wildcard pattern matching functions. */ /* * Copyright (C) 2013, 2015 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include "wimlib/dentry.h" #include "wimlib/encoding.h" #include "wimlib/paths.h" #include "wimlib/pattern.h" static bool string_matches_pattern(const tchar *string, const tchar * const string_end, const tchar *pattern, const tchar * const pattern_end) { for (; string != string_end; string++, pattern++) { if (pattern == pattern_end) return false; if (*pattern == T('*')) { return string_matches_pattern(string, string_end, pattern + 1, pattern_end) || string_matches_pattern(string + 1, string_end, pattern, pattern_end); } if (*string != *pattern && *pattern != T('?') && !(default_ignore_case && totlower(*string) == totlower(*pattern))) return false; } while (pattern != pattern_end && *pattern == T('*')) pattern++; return pattern == pattern_end; } /* Advance past zero or more path separators. */ static const tchar * advance_to_next_component(const tchar *p) { while (*p == WIM_PATH_SEPARATOR) p++; return p; } /* Advance past a nonempty path component. */ static const tchar * advance_through_component(const tchar *p) { do { p++; } while (*p && *p != WIM_PATH_SEPARATOR); return p; } /* * Determine whether a path matches a wildcard pattern. * * @path * The null-terminated path string to match. * @pattern * The null-terminated wildcard pattern to match. It can contain the * wildcard characters '*' (which matches zero or more characters) and '?' * (which matches any single character). If there is no leading path * separator, then the match is attempted with the filename component of * @path only; otherwise, the match is attempted with the entire @path. * @match_flags * MATCH_* flags, see the flag definitions. * * @path and @pattern can both contain path separators (character * WIM_PATH_SEPARATOR). Leading and trailing path separators are not * significant, except when determining whether to match only the filename * component as noted above. The lengths of interior path separator sequences * are not significant. The '*' and '?' characters act within a single path * component only (they do not match path separators). * * Matching is done with the default case sensitivity behavior. * * Returns %true iff the path matched the pattern. */ bool match_path(const tchar *path, const tchar *pattern, int match_flags) { /* Filename only? */ if (*pattern != WIM_PATH_SEPARATOR) path = path_basename(path); for (;;) { const tchar *path_component_end; const tchar *pattern_component_end; path = advance_to_next_component(path); pattern = advance_to_next_component(pattern); /* Is the pattern exhausted? */ if (!*pattern) return !*path || (match_flags & MATCH_RECURSIVELY); /* Is the path exhausted (but not the pattern)? */ if (!*path) return (match_flags & MATCH_ANCESTORS); path_component_end = advance_through_component(path); pattern_component_end = advance_through_component(pattern); /* Do the components match? */ if (!string_matches_pattern(path, path_component_end, pattern, pattern_component_end)) return false; path = path_component_end; pattern = pattern_component_end; } } /* * Expand a path pattern in an in-memory tree of dentries. * * @root * The root of the directory tree in which to expand the pattern. * @pattern * The path pattern to expand, which may contain the '*' and '?' wildcard * characters. Path separators must be WIM_PATH_SEPARATOR. Leading and * trailing path separators are ignored. The default case sensitivity * behavior is used. * @consume_dentry * A callback function which will receive each matched directory entry. * @ctx * Opaque context argument for @consume_dentry. * * @return 0 on success; a positive error code on failure; or the first nonzero * value returned by @consume_dentry. */ int expand_path_pattern(struct wim_dentry *root, const tchar *pattern, int (*consume_dentry)(struct wim_dentry *, void *), void *ctx) { const tchar *pattern_component_end; struct wim_dentry *child; if (!root) return 0; pattern = advance_to_next_component(pattern); /* If there are no more components, then 'root' is matched. */ if (!*pattern) return (*consume_dentry)(root, ctx); pattern_component_end = advance_through_component(pattern); /* For each child dentry that matches the current pattern component, * recurse with the remainder of the pattern. */ for_dentry_child(child, root) { const tchar *name; size_t name_nbytes; int ret; ret = utf16le_get_tstr(child->d_name, child->d_name_nbytes, &name, &name_nbytes); if (ret) return ret; if (string_matches_pattern(name, &name[name_nbytes / sizeof(tchar)], pattern, pattern_component_end)) ret = expand_path_pattern(child, pattern_component_end, consume_dentry, ctx); utf16le_put_tstr(name); if (ret) return ret; } return 0; } wimlib-1.13.1/src/test_support.c0000644000175000017500000012737113376110154013523 00000000000000/* * test_support.c - Supporting code for tests */ /* * Copyright (C) 2015-2018 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ /* * This file contains specialized test code which is only compiled when the * library is configured with --enable-test-support. The major features are: * * - Random directory tree generation * - Directory tree comparison */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef ENABLE_TEST_SUPPORT #include #include #include #include #include #include "wimlib.h" #include "wimlib/endianness.h" #include "wimlib/encoding.h" #include "wimlib/metadata.h" #include "wimlib/dentry.h" #include "wimlib/inode.h" #include "wimlib/object_id.h" #include "wimlib/reparse.h" #include "wimlib/scan.h" #include "wimlib/security_descriptor.h" #include "wimlib/test_support.h" #include "wimlib/unix_data.h" #include "wimlib/xattr.h" /*----------------------------------------------------------------------------* * File tree generation * *----------------------------------------------------------------------------*/ struct generation_context { struct scan_params *params; struct wim_dentry *used_short_names[256]; bool metadata_only; }; static u32 rand32(void) { static u64 state = 0x55DB93D0AB838771; /* A simple linear congruential generator */ state = (state * 25214903917 + 11) & ((1ULL << 48) - 1); return state >> 16; } static bool randbool(void) { return (rand32() & 1) != 0; } static u8 rand8(void) { return (u8)rand32(); } static u16 rand16(void) { return (u16)rand32(); } static u64 rand64(void) { return ((u64)rand32() << 32) | rand32(); } static u64 generate_random_timestamp(void) { /* When setting timestamps on Windows: * - 0 is a special value meaning "not specified" * - if the high bit is set you get STATUS_INVALID_PARAMETER */ return (1 + rand64()) & ~(1ULL << 63); } static inline bool is_valid_windows_filename_char(utf16lechar c) { return le16_to_cpu(c) > 31 && c != cpu_to_le16('/') && c != cpu_to_le16('<') && c != cpu_to_le16('>') && c != cpu_to_le16(':') && c != cpu_to_le16('"') && c != cpu_to_le16('/' ) && c != cpu_to_le16('\\') && c != cpu_to_le16('|') && c != cpu_to_le16('?') && c != cpu_to_le16('*'); } /* Is the character valid in a filename on the current platform? */ static inline bool is_valid_filename_char(utf16lechar c) { #ifdef __WIN32__ return is_valid_windows_filename_char(c); #else return c != cpu_to_le16('\0') && c != cpu_to_le16('/'); #endif } /* Generate a random filename and return its length. */ static int generate_random_filename(utf16lechar name[], int max_len, struct generation_context *ctx) { int len; /* Choose the length of the name. */ switch (rand32() % 8) { default: /* short name */ len = 1 + (rand32() % 6); break; case 2: case 3: case 4: /* medium-length name */ len = 7 + (rand32() % 8); break; case 5: case 6: /* long name */ len = 15 + (rand32() % 15); break; case 7: /* very long name */ len = 30 + (rand32() % 90); break; } len = min(len, max_len); retry: /* Generate the characters in the name. */ for (int i = 0; i < len; i++) { do { name[i] = rand16(); } while (!is_valid_filename_char(name[i])); } /* Add a null terminator. */ name[len] = cpu_to_le16('\0'); /* Don't generate . and .. */ if (name[0] == cpu_to_le16('.') && (len == 1 || (len == 2 && name[1] == cpu_to_le16('.')))) goto retry; return len; } /* The set of characters which are valid in short filenames. */ static const char valid_short_name_chars[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '#', '$', '%', '&', '\'', '(', ')', '-', '@', '^', '_', '`', '{', '}', '~', /* Note: Windows does not allow space and 128-255 in short filenames * (tested on both NTFS and FAT). */ }; static int generate_short_name_component(utf16lechar p[], int len) { for (int i = 0; i < len; i++) { char c = valid_short_name_chars[rand32() % ARRAY_LEN(valid_short_name_chars)]; p[i] = cpu_to_le16(c); } return len; } /* Generate a random short (8.3) filename and return its length. * The @name array must have length >= 13 (8 + 1 + 3 + 1). */ static int generate_random_short_name(utf16lechar name[], struct generation_context *ctx) { /* * Legal short names on Windows consist of 1 to 8 characters, optionally * followed by a dot then 1 to 3 more characters. Only certain * characters are allowed. */ int base_len = 1 + (rand32() % 8); int ext_len = rand32() % 4; int total_len; base_len = generate_short_name_component(name, base_len); if (ext_len) { name[base_len] = cpu_to_le16('.'); ext_len = generate_short_name_component(&name[base_len + 1], ext_len); total_len = base_len + 1 + ext_len; } else { total_len = base_len; } name[total_len] = cpu_to_le16('\0'); return total_len; } static const struct { u8 num_subauthorities; u64 identifier_authority; u32 subauthorities[6]; } common_sids[] = { { 1, 0, {0}}, /* NULL_SID */ { 1, 1, {0}}, /* WORLD_SID */ { 1, 2, {0}}, /* LOCAL_SID */ { 1, 3, {0}}, /* CREATOR_OWNER_SID */ { 1, 3, {1}}, /* CREATOR_GROUP_SID */ { 1, 3, {2}}, /* CREATOR_OWNER_SERVER_SID */ { 1, 3, {3}}, /* CREATOR_GROUP_SERVER_SID */ // { 0, 5, {}}, /* NT_AUTHORITY_SID */ { 1, 5, {1}}, /* DIALUP_SID */ { 1, 5, {2}}, /* NETWORK_SID */ { 1, 5, {3}}, /* BATCH_SID */ { 1, 5, {4}}, /* INTERACTIVE_SID */ { 1, 5, {6}}, /* SERVICE_SID */ { 1, 5, {7}}, /* ANONYMOUS_LOGON_SID */ { 1, 5, {8}}, /* PROXY_SID */ { 1, 5, {9}}, /* SERVER_LOGON_SID */ { 1, 5, {10}}, /* SELF_SID */ { 1, 5, {11}}, /* AUTHENTICATED_USER_SID */ { 1, 5, {12}}, /* RESTRICTED_CODE_SID */ { 1, 5, {13}}, /* TERMINAL_SERVER_SID */ { 1, 5, {18}}, /* NT AUTHORITY\SYSTEM */ { 1, 5, {19}}, /* NT AUTHORITY\LOCAL SERVICE */ { 1, 5, {20}}, /* NT AUTHORITY\NETWORK SERVICE */ { 5 ,80, {956008885, 3418522649, 1831038044, 1853292631, 2271478464}}, /* trusted installer */ { 2 ,5, {32, 544} } /* BUILTIN\ADMINISTRATORS */ }; /* Generate a SID and return its size in bytes. */ static size_t generate_random_sid(wimlib_SID *sid, struct generation_context *ctx) { u32 r = rand32(); sid->revision = 1; if (r & 1) { /* Common SID */ r = (r >> 1) % ARRAY_LEN(common_sids); sid->sub_authority_count = common_sids[r].num_subauthorities; for (int i = 0; i < 6; i++) { sid->identifier_authority[i] = common_sids[r].identifier_authority >> (40 - i * 8); } for (int i = 0; i < common_sids[r].num_subauthorities; i++) sid->sub_authority[i] = cpu_to_le32(common_sids[r].subauthorities[i]); } else { /* Random SID */ sid->sub_authority_count = 1 + ((r >> 1) % 15); for (int i = 0; i < 6; i++) sid->identifier_authority[i] = rand8(); for (int i = 0; i < sid->sub_authority_count; i++) sid->sub_authority[i] = cpu_to_le32(rand32()); } return (u8 *)&sid->sub_authority[sid->sub_authority_count] - (u8 *)sid; } /* Generate an ACL and return its size in bytes. */ static size_t generate_random_acl(wimlib_ACL *acl, bool dacl, struct generation_context *ctx) { u8 *p; u16 ace_count; ace_count = rand32() % 16; acl->revision = 2; acl->sbz1 = 0; acl->ace_count = cpu_to_le16(ace_count); acl->sbz2 = 0; p = (u8 *)(acl + 1); for (int i = 0; i < ace_count; i++) { wimlib_ACCESS_ALLOWED_ACE *ace = (wimlib_ACCESS_ALLOWED_ACE *)p; /* ACCESS_ALLOWED, ACCESS_DENIED, or SYSTEM_AUDIT; format is the * same for all */ if (dacl) ace->hdr.type = rand32() % 2; else ace->hdr.type = 2; ace->hdr.flags = rand8(); ace->mask = cpu_to_le32(rand32() & 0x001F01FF); p += offsetof(wimlib_ACCESS_ALLOWED_ACE, sid) + generate_random_sid(&ace->sid, ctx); ace->hdr.size = cpu_to_le16(p - (u8 *)ace); } acl->acl_size = cpu_to_le16(p - (u8 *)acl); return p - (u8 *)acl; } /* Generate a security descriptor and return its size in bytes. */ static size_t generate_random_security_descriptor(void *_desc, struct generation_context *ctx) { wimlib_SECURITY_DESCRIPTOR_RELATIVE *desc = _desc; u16 control; u8 *p; control = rand16(); control &= (wimlib_SE_DACL_AUTO_INHERITED | wimlib_SE_SACL_AUTO_INHERITED); control |= wimlib_SE_SELF_RELATIVE | wimlib_SE_DACL_PRESENT | wimlib_SE_SACL_PRESENT; desc->revision = 1; desc->sbz1 = 0; desc->control = cpu_to_le16(control); p = (u8 *)(desc + 1); desc->owner_offset = cpu_to_le32(p - (u8 *)desc); p += generate_random_sid((wimlib_SID *)p, ctx); desc->group_offset = cpu_to_le32(p - (u8 *)desc); p += generate_random_sid((wimlib_SID *)p, ctx); if ((control & wimlib_SE_DACL_PRESENT) && randbool()) { desc->dacl_offset = cpu_to_le32(p - (u8 *)desc); p += generate_random_acl((wimlib_ACL *)p, true, ctx); } else { desc->dacl_offset = cpu_to_le32(0); } if ((control & wimlib_SE_SACL_PRESENT) && randbool()) { desc->sacl_offset = cpu_to_le32(p - (u8 *)desc); p += generate_random_acl((wimlib_ACL *)p, false, ctx); } else { desc->sacl_offset = cpu_to_le32(0); } return p - (u8 *)desc; } static bool am_root(void) { #ifdef __WIN32__ return false; #else return (getuid() == 0); #endif } static u32 generate_uid(void) { #ifdef __WIN32__ return 0; #else if (am_root()) return rand32(); return getuid(); #endif } static u32 generate_gid(void) { #ifdef __WIN32__ return 0; #else if (am_root()) return rand32(); return getgid(); #endif } #ifdef __WIN32__ # ifndef S_IFLNK # define S_IFLNK 0120000 # endif # ifndef S_IFSOCK # define S_IFSOCK 0140000 # endif #endif static int set_random_unix_metadata(struct wim_inode *inode) { struct wimlib_unix_data dat; dat.uid = generate_uid(); dat.gid = generate_gid(); if (inode_is_symlink(inode)) dat.mode = S_IFLNK | 0777; else if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) dat.mode = S_IFDIR | 0700 | (rand32() % 07777); else if (is_zero_hash(inode_get_hash_of_unnamed_data_stream(inode)) && randbool() && am_root()) { dat.mode = rand32() % 07777; switch (rand32() % 4) { case 0: dat.mode |= S_IFIFO; break; case 1: dat.mode |= S_IFCHR; dat.rdev = 261; /* /dev/zero */ break; case 2: dat.mode |= S_IFBLK; dat.rdev = 261; /* /dev/zero */ break; default: dat.mode |= S_IFSOCK; break; } } else { dat.mode = S_IFREG | 0400 | (rand32() % 07777); } dat.rdev = 0; if (!inode_set_unix_data(inode, &dat, UNIX_DATA_ALL)) return WIMLIB_ERR_NOMEM; return 0; } static noinline_for_stack int set_random_xattrs(struct wim_inode *inode) { int num_xattrs = 1 + rand32() % 16; char entries[8192]; struct wim_xattr_entry *entry = (void *)entries; size_t entries_size; struct wimlib_unix_data unix_data; #ifdef __WIN32__ const char *prefix = ""; #else const char *prefix = "user."; #endif static const char capability_name[] = "security.capability"; bool generated_capability_xattr = false; /* * On Linux, xattrs in the "user" namespace are only permitted on * regular files and directories. For other types of files we can use * the "trusted" namespace, but this requires root. */ if (inode_is_symlink(inode) || (inode_get_unix_data(inode, &unix_data) && !S_ISREG(unix_data.mode) && !S_ISDIR(unix_data.mode))) { if (!am_root()) return 0; prefix = "trusted."; } for (int i = 0; i < num_xattrs; i++) { int value_len = rand32() % 64; u8 *p; #ifdef __WIN32__ if (value_len == 0) value_len++; #endif entry->value_len = cpu_to_le16(value_len); entry->flags = 0; if (rand32() % 16 == 0 && am_root() && !generated_capability_xattr) { int name_len = sizeof(capability_name) - 1; entry->name_len = name_len; p = mempcpy(entry->name, capability_name, name_len + 1); generated_capability_xattr = true; } else { int name_len = 1 + rand32() % 64; entry->name_len = strlen(prefix) + name_len; p = mempcpy(entry->name, prefix, strlen(prefix)); *p++ = 'A' + i; for (int j = 1; j < name_len; j++) { do { #ifdef __WIN32__ *p = 'A' + rand8() % 26; #else *p = rand8(); #endif } while (*p == '\0'); p++; } *p++ = '\0'; } for (int j = 0; j < value_len; j++) *p++ = rand8(); entry = (void *)p; } entries_size = (char *)entry - entries; wimlib_assert(entries_size > 0 && entries_size <= sizeof(entries)); if (!inode_set_xattrs(inode, entries, entries_size)) return WIMLIB_ERR_NOMEM; return 0; } static int set_random_metadata(struct wim_inode *inode, struct generation_context *ctx) { u32 attrib = (rand32() & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_SPARSE_FILE)); /* File attributes */ inode->i_attributes |= attrib; /* Timestamps */ inode->i_creation_time = generate_random_timestamp(); inode->i_last_access_time = generate_random_timestamp(); inode->i_last_write_time = generate_random_timestamp(); /* Security descriptor */ if (randbool()) { char desc[8192] _aligned_attribute(8); size_t size; size = generate_random_security_descriptor(desc, ctx); wimlib_assert(size <= sizeof(desc)); inode->i_security_id = sd_set_add_sd(ctx->params->sd_set, desc, size); if (unlikely(inode->i_security_id < 0)) return WIMLIB_ERR_NOMEM; } /* Object ID */ if (rand32() % 32 == 0) { struct wimlib_object_id object_id; for (int i = 0; i < sizeof(object_id); i++) *((u8 *)&object_id + i) = rand8(); if (!inode_set_object_id(inode, &object_id, sizeof(object_id))) return WIMLIB_ERR_NOMEM; } /* Standard UNIX permissions and special files */ if (rand32() % 16 == 0) { int ret = set_random_unix_metadata(inode); if (ret) return ret; } /* Extended attributes */ if (rand32() % 32 == 0) { int ret = set_random_xattrs(inode); if (ret) return ret; } return 0; } /* Choose a random size for generated file data. We want to usually generate * empty, small, or medium files, but occasionally generate large files. */ static size_t select_stream_size(struct generation_context *ctx) { if (ctx->metadata_only) return 0; switch (rand32() % 2048) { default: /* Empty */ return 0; case 600 ... 799: /* Microscopic */ return rand32() % 64; case 800 ... 1319: /* Tiny */ return rand32() % 4096; case 1320 ... 1799: /* Small */ return rand32() % 32768; case 1800 ... 2046: /* Medium */ return rand32() % 262144; case 2047: /* Large */ return rand32() % 134217728; } } /* Fill 'buffer' with 'size' bytes of "interesting" file data. */ static void generate_data(u8 *buffer, size_t size, struct generation_context *ctx) { size_t mask = -1; size_t num_byte_fills = rand32() % 256; if (size == 0) return; /* Start by initializing to a random byte */ memset(buffer, rand32() % 256, size); /* Add some random bytes in some random places */ for (size_t i = 0; i < num_byte_fills; i++) { u8 b = rand8(); size_t count = ((double)size / (double)num_byte_fills) * ((double)rand32() / 2e9); size_t offset = rand32() & ~mask; while (count--) { buffer[(offset + ((rand32()) & mask)) % size] = b; } if (rand32() % 4 == 0) mask = (size_t)-1 << rand32() % 4; } /* Sometimes add a wave pattern */ if (rand32() % 8 == 0) { double magnitude = rand32() % 128; double scale = 1.0 / (1 + (rand32() % 256)); for (size_t i = 0; i < size; i++) buffer[i] += (int)(magnitude * cos(i * scale)); } /* Sometimes add some zero regions (holes) */ if (rand32() % 4 == 0) { size_t num_holes = 1 + (rand32() % 16); for (size_t i = 0; i < num_holes; i++) { size_t hole_offset = rand32() % size; size_t hole_len = min(size - hole_offset, size / (1 + (rand32() % 16))); memset(&buffer[hole_offset], 0, hole_len); } } } static noinline_for_stack int set_random_reparse_point(struct wim_inode *inode, struct generation_context *ctx) { struct reparse_buffer_disk rpbuf; size_t rpdatalen; inode->i_attributes |= FILE_ATTRIBUTE_REPARSE_POINT; if (randbool()) { /* Symlink */ int target_nchars; utf16lechar *targets = (utf16lechar *)rpbuf.link.symlink.data; inode->i_reparse_tag = WIM_IO_REPARSE_TAG_SYMLINK; target_nchars = generate_random_filename(targets, 255, ctx); rpbuf.link.substitute_name_offset = cpu_to_le16(0); rpbuf.link.substitute_name_nbytes = cpu_to_le16(2*target_nchars); rpbuf.link.print_name_offset = cpu_to_le16(2*(target_nchars + 1)); rpbuf.link.print_name_nbytes = cpu_to_le16(2*target_nchars); targets[target_nchars] = cpu_to_le16(0); memcpy(&targets[target_nchars + 1], targets, 2*target_nchars); targets[target_nchars + 1 + target_nchars] = cpu_to_le16(0); rpbuf.link.symlink.flags = cpu_to_le32(SYMBOLIC_LINK_RELATIVE); rpdatalen = ((u8 *)targets - rpbuf.rpdata) + 2*(target_nchars + 1 + target_nchars + 1); } else { rpdatalen = select_stream_size(ctx) % REPARSE_DATA_MAX_SIZE; generate_data(rpbuf.rpdata, rpdatalen, ctx); if (rpdatalen >= GUID_SIZE && randbool()) { /* Non-Microsoft reparse tag (16-byte GUID required) */ u8 *guid = rpbuf.rpdata; guid[6] = (guid[6] & 0x0F) | 0x40; guid[8] = (guid[8] & 0x3F) | 0x80; inode->i_reparse_tag = 0x00000100; } else { /* Microsoft reparse tag */ inode->i_reparse_tag = 0x80000000; } inode->i_rp_reserved = rand16(); } wimlib_assert(rpdatalen < REPARSE_DATA_MAX_SIZE); if (!inode_add_stream_with_data(inode, STREAM_TYPE_REPARSE_POINT, NO_STREAM_NAME, rpbuf.rpdata, rpdatalen, ctx->params->blob_table)) return WIMLIB_ERR_NOMEM; return 0; } static int add_random_data_stream(struct wim_inode *inode, struct generation_context *ctx, const utf16lechar *stream_name) { void *buffer = NULL; size_t size; int ret; size = select_stream_size(ctx); if (size) { buffer = MALLOC(size); if (!buffer) return WIMLIB_ERR_NOMEM; generate_data(buffer, size, ctx); } ret = 0; if (!inode_add_stream_with_data(inode, STREAM_TYPE_DATA, stream_name, buffer, size, ctx->params->blob_table)) ret = WIMLIB_ERR_NOMEM; FREE(buffer); return ret; } static int set_random_streams(struct wim_inode *inode, struct generation_context *ctx) { int ret; u32 r; /* Reparse point (sometimes) */ if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) { ret = set_random_reparse_point(inode, ctx); if (ret) return ret; } /* Unnamed data stream (nondirectories and non-symlinks only) */ if (!(inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) && !inode_is_symlink(inode)) { ret = add_random_data_stream(inode, ctx, NO_STREAM_NAME); if (ret) return ret; } /* Named data streams (sometimes) */ r = rand32() % 256; if (r > 230) { utf16lechar stream_name[2] = {cpu_to_le16('a'), '\0'}; r -= 230; while (r--) { ret = add_random_data_stream(inode, ctx, stream_name); if (ret) return ret; stream_name[0] += cpu_to_le16(1); } } return 0; } static u64 select_inode_number(struct generation_context *ctx) { const struct wim_inode_table *table = ctx->params->inode_table; const struct hlist_head *head; const struct wim_inode *inode; head = &table->array[rand32() % table->capacity]; hlist_for_each_entry(inode, head, i_hlist_node) if (randbool()) return inode->i_ino; return rand32(); } static u32 select_num_children(u32 depth, struct generation_context *ctx) { const double b = 1.01230; u32 r = rand32() % 500; return ((pow(b, pow(b, r)) - 1) / pow(depth, 1.5)) + (2 - exp(0.04/depth)); } static bool is_name_valid_in_win32_namespace(const utf16lechar *name) { const utf16lechar *p; static const char * const reserved_names[] = { "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", }; /* The name must be nonempty. */ if (!name || !*name) return false; /* All characters must be valid on Windows. */ for (p = name; *p; p++) if (!is_valid_windows_filename_char(*p)) return false; /* Note: a trailing dot or space is permitted, even though on Windows * such a file can only be accessed using a WinNT-style path. */ /* The name can't be one of the reserved names or be a reserved name * with an extension. Case insensitive. */ for (size_t i = 0; i < ARRAY_LEN(reserved_names); i++) { for (size_t j = 0; ; j++) { u16 c1 = le16_to_cpu(name[j]); u16 c2 = reserved_names[i][j]; if (c2 == '\0') { if (c1 == '\0' || c1 == '.') return false; break; } if (upcase[c1] != upcase[c2]) break; } } return true; } static int set_random_short_name(struct wim_dentry *dir, struct wim_dentry *child, struct generation_context *ctx) { utf16lechar name[12 + 1]; int name_len; u32 hash; struct wim_dentry **bucket; /* If the long name is not allowed in the Win32 namespace, then it * cannot be assigned a corresponding short name. */ if (!is_name_valid_in_win32_namespace(child->d_name)) return 0; retry: /* Don't select a short name that is already used by a long name within * the same directory. */ do { name_len = generate_random_short_name(name, ctx); } while (get_dentry_child_with_utf16le_name(dir, name, name_len * 2, WIMLIB_CASE_INSENSITIVE)); /* Don't select a short name that is already used by another short name * within the same directory. */ hash = 0; for (const utf16lechar *p = name; *p; p++) hash = (hash * 31) + *p; FREE(child->d_short_name); child->d_short_name = memdup(name, (name_len + 1) * 2); child->d_short_name_nbytes = name_len * 2; if (!child->d_short_name) return WIMLIB_ERR_NOMEM; bucket = &ctx->used_short_names[hash % ARRAY_LEN(ctx->used_short_names)]; for (struct wim_dentry *d = *bucket; d != NULL; d = d->d_next_extraction_alias) { if (!cmp_utf16le_strings(child->d_short_name, name_len, d->d_short_name, d->d_short_name_nbytes / 2, true)) { goto retry; } } if (!is_name_valid_in_win32_namespace(child->d_short_name)) goto retry; child->d_next_extraction_alias = *bucket; *bucket = child; return 0; } static bool inode_has_short_name(const struct wim_inode *inode) { const struct wim_dentry *dentry; inode_for_each_dentry(dentry, inode) if (dentry_has_short_name(dentry)) return true; return false; } static int generate_dentry_tree_recursive(struct wim_dentry *dir, u32 depth, struct generation_context *ctx) { u32 num_children = select_num_children(depth, ctx); struct wim_dentry *child; int ret; memset(ctx->used_short_names, 0, sizeof(ctx->used_short_names)); /* Generate 'num_children' dentries within 'dir'. Some may be * directories themselves. */ for (u32 i = 0; i < num_children; i++) { /* Generate the next child dentry. */ struct wim_inode *inode; u64 ino; bool is_directory = (rand32() % 16 <= 6); bool is_reparse = (rand32() % 8 == 0); utf16lechar name[63 + 1]; /* for UNIX extraction: 63 * 4 <= 255 */ int name_len; struct wim_dentry *duplicate; /* * Select an inode number for the new file. Sometimes choose an * existing inode number (i.e. create a hard link). However, * wimlib intentionally doesn't honor directory hard links, and * reparse points cannot be represented in the WIM file format * at all; so don't create hard links for such files. */ if (is_directory || is_reparse) ino = 0; else ino = select_inode_number(ctx); /* Create the dentry. */ ret = inode_table_new_dentry(ctx->params->inode_table, NULL, ino, 0, ino == 0, &child); if (ret) return ret; /* Choose a filename that is unique within the directory.*/ do { name_len = generate_random_filename(name, ARRAY_LEN(name) - 1, ctx); } while (get_dentry_child_with_utf16le_name(dir, name, name_len * 2, WIMLIB_CASE_PLATFORM_DEFAULT)); ret = dentry_set_name_utf16le(child, name, name_len * 2); if (ret) { free_dentry(child); return ret; } /* Add the dentry to the directory. */ duplicate = dentry_add_child(dir, child); wimlib_assert(!duplicate); inode = child->d_inode; if (inode->i_nlink > 1) /* Existing inode? */ continue; /* New inode; set attributes, metadata, and data. */ if (is_directory) inode->i_attributes |= FILE_ATTRIBUTE_DIRECTORY; if (is_reparse) inode->i_attributes |= FILE_ATTRIBUTE_REPARSE_POINT; ret = set_random_streams(inode, ctx); if (ret) return ret; ret = set_random_metadata(inode, ctx); if (ret) return ret; /* Recurse if it's a directory. */ if (is_directory && !is_reparse) { ret = generate_dentry_tree_recursive(child, depth + 1, ctx); if (ret) return ret; } } for_dentry_child(child, dir) { /* sometimes generate a unique short name */ if (randbool() && !inode_has_short_name(child->d_inode)) { ret = set_random_short_name(dir, child, ctx); if (ret) return ret; } } return 0; } int generate_dentry_tree(struct wim_dentry **root_ret, const tchar *_ignored, struct scan_params *params) { int ret; struct wim_dentry *root = NULL; struct generation_context ctx = { .params = params, }; ctx.metadata_only = ((rand32() % 8) != 0); /* usually metadata only */ ret = inode_table_new_dentry(params->inode_table, NULL, 0, 0, true, &root); if (!ret) { root->d_inode->i_attributes = FILE_ATTRIBUTE_DIRECTORY; ret = set_random_streams(root->d_inode, &ctx); } if (!ret) ret = set_random_metadata(root->d_inode, &ctx); if (!ret) ret = generate_dentry_tree_recursive(root, 1, &ctx); if (!ret) *root_ret = root; else free_dentry_tree(root, params->blob_table); return ret; } /*----------------------------------------------------------------------------* * File tree comparison * *----------------------------------------------------------------------------*/ #define INDEX_NODE_TO_DENTRY(node) \ ((node) ? avl_tree_entry((node), struct wim_dentry, d_index_node) : NULL) static struct wim_dentry * dentry_first_child(struct wim_dentry *dentry) { return INDEX_NODE_TO_DENTRY( avl_tree_first_in_order(dentry->d_inode->i_children)); } static struct wim_dentry * dentry_next_sibling(struct wim_dentry *dentry) { return INDEX_NODE_TO_DENTRY( avl_tree_next_in_order(&dentry->d_index_node)); } /* * Verify that the dentries in the tree 'd1' exactly match the dentries in the * tree 'd2', considering long and short filenames. In addition, set * 'd_corresponding' of each dentry to point to the corresponding dentry in the * other tree, and set 'i_corresponding' of each inode to point to the * unverified corresponding inode in the other tree. */ static int calc_corresponding_files_recursive(struct wim_dentry *d1, struct wim_dentry *d2, int cmp_flags) { struct wim_dentry *child1; struct wim_dentry *child2; int ret; /* Compare long filenames, case sensitively. */ if (cmp_utf16le_strings(d1->d_name, d1->d_name_nbytes / 2, d2->d_name, d2->d_name_nbytes / 2, false)) { ERROR("Filename mismatch; path1=\"%"TS"\", path2=\"%"TS"\"", dentry_full_path(d1), dentry_full_path(d2)); return WIMLIB_ERR_IMAGES_ARE_DIFFERENT; } /* Compare short filenames, case insensitively. */ if (!(d2->d_short_name_nbytes == 0 && (cmp_flags & WIMLIB_CMP_FLAG_UNIX_MODE)) && cmp_utf16le_strings(d1->d_short_name, d1->d_short_name_nbytes / 2, d2->d_short_name, d2->d_short_name_nbytes / 2, true)) { ERROR("Short name mismatch; path=\"%"TS"\"", dentry_full_path(d1)); return WIMLIB_ERR_IMAGES_ARE_DIFFERENT; } /* Match up the dentries */ d1->d_corresponding = d2; d2->d_corresponding = d1; /* Match up the inodes (may overwrite previous value) */ d1->d_inode->i_corresponding = d2->d_inode; d2->d_inode->i_corresponding = d1->d_inode; /* Process children */ child1 = dentry_first_child(d1); child2 = dentry_first_child(d2); while (child1 || child2) { if (!child1 || !child2) { ERROR("Child count mismatch; " "path1=\"%"TS"\", path2=\"%"TS"\"", dentry_full_path(d1), dentry_full_path(d2)); return WIMLIB_ERR_IMAGES_ARE_DIFFERENT; } /* Recurse on this pair of children. */ ret = calc_corresponding_files_recursive(child1, child2, cmp_flags); if (ret) return ret; /* Continue to the next pair of children. */ child1 = dentry_next_sibling(child1); child2 = dentry_next_sibling(child2); } return 0; } /* Perform sanity checks on an image's inodes. All assertions here should pass, * even if the images being compared are different. */ static void assert_inodes_sane(const struct wim_image_metadata *imd) { const struct wim_inode *inode; const struct wim_dentry *dentry; size_t link_count; image_for_each_inode(inode, imd) { link_count = 0; inode_for_each_dentry(dentry, inode) { wimlib_assert(dentry->d_inode == inode); link_count++; } wimlib_assert(link_count > 0); wimlib_assert(link_count == inode->i_nlink); wimlib_assert(inode->i_corresponding != NULL); } } static int check_hard_link(struct wim_dentry *dentry, void *_ignore) { /* My inode is my corresponding dentry's inode's corresponding inode, * and my inode's corresponding inode is my corresponding dentry's * inode. */ const struct wim_inode *a = dentry->d_inode; const struct wim_inode *b = dentry->d_corresponding->d_inode; if (a == b->i_corresponding && a->i_corresponding == b) return 0; ERROR("Hard link difference; path=%"TS"", dentry_full_path(dentry)); return WIMLIB_ERR_IMAGES_ARE_DIFFERENT; } static const struct { u32 flag; const char *name; } file_attr_flags[] = { {FILE_ATTRIBUTE_READONLY, "READONLY"}, {FILE_ATTRIBUTE_HIDDEN, "HIDDEN"}, {FILE_ATTRIBUTE_SYSTEM, "SYSTEM"}, {FILE_ATTRIBUTE_DIRECTORY, "DIRECTORY"}, {FILE_ATTRIBUTE_ARCHIVE, "ARCHIVE"}, {FILE_ATTRIBUTE_DEVICE, "DEVICE"}, {FILE_ATTRIBUTE_NORMAL, "NORMAL"}, {FILE_ATTRIBUTE_TEMPORARY, "TEMPORARY"}, {FILE_ATTRIBUTE_SPARSE_FILE, "SPARSE_FILE"}, {FILE_ATTRIBUTE_REPARSE_POINT, "REPARSE_POINT"}, {FILE_ATTRIBUTE_COMPRESSED, "COMPRESSED"}, {FILE_ATTRIBUTE_OFFLINE, "OFFLINE"}, {FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, "NOT_CONTENT_INDEXED"}, {FILE_ATTRIBUTE_ENCRYPTED, "ENCRYPTED"}, {FILE_ATTRIBUTE_VIRTUAL, "VIRTUAL"}, }; static int cmp_attributes(const struct wim_inode *inode1, const struct wim_inode *inode2, int cmp_flags) { const u32 changed = inode1->i_attributes ^ inode2->i_attributes; const u32 set = inode2->i_attributes & ~inode1->i_attributes; const u32 cleared = inode1->i_attributes & ~inode2->i_attributes; /* NORMAL may change, but it must never be set along with other * attributes. */ if ((inode2->i_attributes & FILE_ATTRIBUTE_NORMAL) && (inode2->i_attributes & ~FILE_ATTRIBUTE_NORMAL)) goto mismatch; /* DIRECTORY may change in UNIX mode for symlinks. */ if (changed & FILE_ATTRIBUTE_DIRECTORY) { if (!(inode_is_symlink(inode1) && (cmp_flags & WIMLIB_CMP_FLAG_UNIX_MODE))) goto mismatch; } /* REPARSE_POINT may be cleared in UNIX mode if the inode is not a * symlink. */ if ((changed & FILE_ATTRIBUTE_REPARSE_POINT) && !((cleared & FILE_ATTRIBUTE_REPARSE_POINT) && (cmp_flags & WIMLIB_CMP_FLAG_UNIX_MODE) && !inode_is_symlink(inode1))) goto mismatch; /* SPARSE_FILE may be cleared in UNIX and NTFS-3G modes, or in Windows * mode if the inode is a directory. */ if ((changed & FILE_ATTRIBUTE_SPARSE_FILE) && !((cleared & FILE_ATTRIBUTE_SPARSE_FILE) && ((cmp_flags & (WIMLIB_CMP_FLAG_UNIX_MODE | WIMLIB_CMP_FLAG_NTFS_3G_MODE)) || ((cmp_flags & WIMLIB_CMP_FLAG_WINDOWS_MODE) && (inode1->i_attributes & FILE_ATTRIBUTE_DIRECTORY))))) goto mismatch; /* COMPRESSED may change in UNIX and NTFS-3G modes. (It *should* be * preserved in NTFS-3G mode, but it's not implemented yet.) */ if ((changed & FILE_ATTRIBUTE_COMPRESSED) && !(cmp_flags & (WIMLIB_CMP_FLAG_UNIX_MODE | WIMLIB_CMP_FLAG_NTFS_3G_MODE))) goto mismatch; /* All other attributes can change in UNIX mode, but not in any other * mode. */ if ((changed & ~(FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_SPARSE_FILE | FILE_ATTRIBUTE_COMPRESSED)) && !(cmp_flags & WIMLIB_CMP_FLAG_UNIX_MODE)) goto mismatch; return 0; mismatch: ERROR("Attribute mismatch for %"TS": 0x%08"PRIx32" vs. 0x%08"PRIx32":", inode_any_full_path(inode1), inode1->i_attributes, inode2->i_attributes); for (size_t i = 0; i < ARRAY_LEN(file_attr_flags); i++) { u32 flag = file_attr_flags[i].flag; if (changed & flag) { fprintf(stderr, "\tFILE_ATTRIBUTE_%s was %s\n", file_attr_flags[i].name, (set & flag) ? "set" : "cleared"); } } return WIMLIB_ERR_IMAGES_ARE_DIFFERENT; } static int cmp_object_ids(const struct wim_inode *inode1, const struct wim_inode *inode2, int cmp_flags) { const void *objid1, *objid2; u32 len1, len2; objid1 = inode_get_object_id(inode1, &len1); objid2 = inode_get_object_id(inode2, &len2); if (!objid1 && !objid2) return 0; if (objid1 && !objid2) { if (cmp_flags & WIMLIB_CMP_FLAG_UNIX_MODE) return 0; ERROR("%"TS" unexpectedly lost its object ID", inode_any_full_path(inode1)); return WIMLIB_ERR_IMAGES_ARE_DIFFERENT; } if (!objid1 && objid2) { ERROR("%"TS" unexpectedly gained an object ID", inode_any_full_path(inode1)); return WIMLIB_ERR_IMAGES_ARE_DIFFERENT; } if (len1 != len2 || memcmp(objid1, objid2, len1) != 0) { ERROR("Object ID of %"TS" differs", inode_any_full_path(inode1)); fprintf(stderr, "objid1="); print_byte_field(objid1, len1, stderr); fprintf(stderr, "\nobjid2="); print_byte_field(objid2, len2, stderr); fprintf(stderr, "\n"); return WIMLIB_ERR_IMAGES_ARE_DIFFERENT; } return 0; } static int cmp_unix_metadata(const struct wim_inode *inode1, const struct wim_inode *inode2, int cmp_flags) { struct wimlib_unix_data dat1, dat2; bool present1, present2; present1 = inode_get_unix_data(inode1, &dat1); present2 = inode_get_unix_data(inode2, &dat2); if (!present1 && !present2) return 0; if (present1 && !present2) { if (cmp_flags & (WIMLIB_CMP_FLAG_NTFS_3G_MODE | WIMLIB_CMP_FLAG_WINDOWS_MODE)) return 0; ERROR("%"TS" unexpectedly lost its UNIX metadata", inode_any_full_path(inode1)); return WIMLIB_ERR_IMAGES_ARE_DIFFERENT; } if (!present1 && present2) { if (cmp_flags & WIMLIB_CMP_FLAG_UNIX_MODE) return 0; ERROR("%"TS" unexpectedly gained UNIX metadata", inode_any_full_path(inode1)); return WIMLIB_ERR_IMAGES_ARE_DIFFERENT; } if (memcmp(&dat1, &dat2, sizeof(dat1)) != 0) { ERROR("UNIX metadata of %"TS" differs: " "[uid=%u, gid=%u, mode=0%o, rdev=%u] vs. " "[uid=%u, gid=%u, mode=0%o, rdev=%u]", inode_any_full_path(inode1), dat1.uid, dat1.gid, dat1.mode, dat1.rdev, dat2.uid, dat2.gid, dat2.mode, dat2.rdev); return WIMLIB_ERR_IMAGES_ARE_DIFFERENT; } return 0; } static int cmp_xattr_names(const void *p1, const void *p2) { const struct wim_xattr_entry *entry1 = *(const struct wim_xattr_entry **)p1; const struct wim_xattr_entry *entry2 = *(const struct wim_xattr_entry **)p2; int res; res = entry1->name_len - entry2->name_len; if (res) return res; return memcmp(entry1->name, entry2->name, entry1->name_len); } /* Validate and sort by name a list of extended attributes */ static int parse_xattrs(const void *xattrs, u32 len, const struct wim_xattr_entry *entries[], u32 *num_entries_p) { u32 limit = *num_entries_p; u32 num_entries = 0; const struct wim_xattr_entry *entry = xattrs; while ((void *)entry < xattrs + len) { if (!valid_xattr_entry(entry, xattrs + len - (void *)entry)) { ERROR("Invalid xattr entry"); return WIMLIB_ERR_INVALID_XATTR; } if (num_entries >= limit) { ERROR("Too many xattr entries"); return WIMLIB_ERR_INVALID_XATTR; } entries[num_entries++] = entry; entry = xattr_entry_next(entry); } if (num_entries == 0) { ERROR("No xattr entries"); return WIMLIB_ERR_INVALID_XATTR; } qsort(entries, num_entries, sizeof(entries[0]), cmp_xattr_names); for (u32 i = 1; i < num_entries; i++) { if (cmp_xattr_names(&entries[i - 1], &entries[i]) == 0) { ERROR("Duplicate xattr names"); return WIMLIB_ERR_INVALID_XATTR; } } *num_entries_p = num_entries; return 0; } static int cmp_xattrs(const struct wim_inode *inode1, const struct wim_inode *inode2, int cmp_flags) { const void *xattrs1, *xattrs2; u32 len1, len2; xattrs1 = inode_get_xattrs(inode1, &len1); xattrs2 = inode_get_xattrs(inode2, &len2); if (!xattrs1 && !xattrs2) { return 0; } else if (xattrs1 && !xattrs2) { if (cmp_flags & WIMLIB_CMP_FLAG_NTFS_3G_MODE) return 0; ERROR("%"TS" unexpectedly lost its xattrs", inode_any_full_path(inode1)); return WIMLIB_ERR_IMAGES_ARE_DIFFERENT; } else if (!xattrs1 && xattrs2) { ERROR("%"TS" unexpectedly gained xattrs", inode_any_full_path(inode1)); return WIMLIB_ERR_IMAGES_ARE_DIFFERENT; } else { const int max_entries = 64; const struct wim_xattr_entry *entries1[max_entries]; const struct wim_xattr_entry *entries2[max_entries]; u32 xattr_count1 = max_entries; u32 xattr_count2 = max_entries; int ret; ret = parse_xattrs(xattrs1, len1, entries1, &xattr_count1); if (ret) { ERROR("%"TS": invalid xattrs", inode_any_full_path(inode1)); return ret; } ret = parse_xattrs(xattrs2, len2, entries2, &xattr_count2); if (ret) { ERROR("%"TS": invalid xattrs", inode_any_full_path(inode2)); return ret; } if (xattr_count1 != xattr_count2) { ERROR("%"TS": number of xattrs changed. had %u " "before, now has %u", inode_any_full_path(inode1), xattr_count1, xattr_count2); } for (u32 i = 0; i < xattr_count1; i++) { const struct wim_xattr_entry *entry1 = entries1[i]; const struct wim_xattr_entry *entry2 = entries2[i]; if (entry1->value_len != entry2->value_len || entry1->name_len != entry2->name_len || entry1->flags != entry2->flags || memcmp(entry1->name, entry2->name, entry1->name_len) || memcmp(entry1->name + entry1->name_len + 1, entry2->name + entry2->name_len + 1, le16_to_cpu(entry1->value_len))) { ERROR("xattr %.*s of %"TS" differs", entry1->name_len, entry1->name, inode_any_full_path(inode1)); return WIMLIB_ERR_IMAGES_ARE_DIFFERENT; } } return 0; } } static int cmp_timestamps(const struct wim_inode *inode1, const struct wim_inode *inode2, int cmp_flags) { if (inode1->i_creation_time != inode2->i_creation_time && !(cmp_flags & WIMLIB_CMP_FLAG_UNIX_MODE)) { ERROR("Creation time of %"TS" differs", inode_any_full_path(inode1)); return WIMLIB_ERR_IMAGES_ARE_DIFFERENT; } if (inode1->i_last_write_time != inode2->i_last_write_time) { ERROR("Last write time of %"TS" differs", inode_any_full_path(inode1)); return WIMLIB_ERR_IMAGES_ARE_DIFFERENT; } if (inode1->i_last_access_time != inode2->i_last_access_time) { ERROR("Last access time of %"TS" differs", inode_any_full_path(inode1)); return WIMLIB_ERR_IMAGES_ARE_DIFFERENT; } return 0; } static int cmp_inodes(const struct wim_inode *inode1, const struct wim_inode *inode2, const struct wim_image_metadata *imd1, const struct wim_image_metadata *imd2, int cmp_flags) { int ret; /* Compare attributes */ ret = cmp_attributes(inode1, inode2, cmp_flags); if (ret) return ret; /* Compare security descriptors */ if (inode_has_security_descriptor(inode1)) { if (inode_has_security_descriptor(inode2)) { const void *desc1 = imd1->security_data->descriptors[inode1->i_security_id]; const void *desc2 = imd2->security_data->descriptors[inode2->i_security_id]; size_t size1 = imd1->security_data->sizes[inode1->i_security_id]; size_t size2 = imd2->security_data->sizes[inode2->i_security_id]; if (size1 != size2 || memcmp(desc1, desc2, size1)) { ERROR("Security descriptor of %"TS" differs!", inode_any_full_path(inode1)); return WIMLIB_ERR_IMAGES_ARE_DIFFERENT; } } else if (!(cmp_flags & WIMLIB_CMP_FLAG_UNIX_MODE)) { ERROR("%"TS" has a security descriptor in the first image but " "not in the second image!", inode_any_full_path(inode1)); return WIMLIB_ERR_IMAGES_ARE_DIFFERENT; } } else if (inode_has_security_descriptor(inode2)) { /* okay --- consider it acceptable if a default security * descriptor was assigned */ /*ERROR("%"TS" has a security descriptor in the second image but "*/ /*"not in the first image!", inode_any_full_path(inode1));*/ /*return WIMLIB_ERR_IMAGES_ARE_DIFFERENT;*/ } /* Compare streams */ for (unsigned i = 0; i < inode1->i_num_streams; i++) { const struct wim_inode_stream *strm1 = &inode1->i_streams[i]; const struct wim_inode_stream *strm2; if (strm1->stream_type == STREAM_TYPE_REPARSE_POINT && (cmp_flags & WIMLIB_CMP_FLAG_UNIX_MODE && !inode_is_symlink(inode1))) continue; if (strm1->stream_type == STREAM_TYPE_UNKNOWN) continue; /* Get the corresponding stream from the second file */ strm2 = inode_get_stream(inode2, strm1->stream_type, strm1->stream_name); if (!strm2) { /* Corresponding stream not found */ if (stream_is_named(strm1) && (cmp_flags & WIMLIB_CMP_FLAG_UNIX_MODE)) continue; ERROR("Stream of %"TS" is missing in second image; " "type %d, named=%d, empty=%d", inode_any_full_path(inode1), strm1->stream_type, stream_is_named(strm1), is_zero_hash(stream_hash(strm1))); return WIMLIB_ERR_IMAGES_ARE_DIFFERENT; } if (!hashes_equal(stream_hash(strm1), stream_hash(strm2))) { ERROR("Stream of %"TS" differs; type %d", inode_any_full_path(inode1), strm1->stream_type); return WIMLIB_ERR_IMAGES_ARE_DIFFERENT; } } /* Compare object IDs */ ret = cmp_object_ids(inode1, inode2, cmp_flags); if (ret) return ret; /* Compare timestamps */ ret = cmp_timestamps(inode1, inode2, cmp_flags); if (ret) return ret; /* Compare standard UNIX metadata */ ret = cmp_unix_metadata(inode1, inode2, cmp_flags); if (ret) return ret; /* Compare extended attributes */ ret = cmp_xattrs(inode1, inode2, cmp_flags); if (ret) return ret; return 0; } static int cmp_images(const struct wim_image_metadata *imd1, const struct wim_image_metadata *imd2, int cmp_flags) { struct wim_dentry *root1 = imd1->root_dentry; struct wim_dentry *root2 = imd2->root_dentry; const struct wim_inode *inode; int ret; ret = calc_corresponding_files_recursive(root1, root2, cmp_flags); if (ret) return ret; /* Verify that the hard links match up between the two images. */ assert_inodes_sane(imd1); assert_inodes_sane(imd2); ret = for_dentry_in_tree(root1, check_hard_link, NULL); if (ret) return ret; /* Compare corresponding inodes. */ image_for_each_inode(inode, imd1) { ret = cmp_inodes(inode, inode->i_corresponding, imd1, imd2, cmp_flags); if (ret) return ret; } return 0; } static int load_image(WIMStruct *wim, int image, struct wim_image_metadata **imd_ret) { int ret = select_wim_image(wim, image); if (!ret) { *imd_ret = wim_get_current_image_metadata(wim); mark_image_dirty(*imd_ret); } return ret; } WIMLIBAPI int wimlib_compare_images(WIMStruct *wim1, int image1, WIMStruct *wim2, int image2, int cmp_flags) { int ret; struct wim_image_metadata *imd1, *imd2; ret = load_image(wim1, image1, &imd1); if (!ret) ret = load_image(wim2, image2, &imd2); if (!ret) ret = cmp_images(imd1, imd2, cmp_flags); return ret; } #endif /* ENABLE_TEST_SUPPORT */ wimlib-1.13.1/src/ntfs-3g_capture.c0000644000175000017500000005734713272231267013765 00000000000000/* * ntfs-3g_capture.c * * Capture a WIM image directly from an NTFS volume using libntfs-3g. We capture * everything we can, including security data and alternate data streams. */ /* * Copyright (C) 2012-2017 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef WITH_NTFS_3G #include #include #include /* for ENODATA, if needed */ #include #include #include #include #include "wimlib/alloca.h" #include "wimlib/assert.h" #include "wimlib/blob_table.h" #include "wimlib/dentry.h" #include "wimlib/encoding.h" #include "wimlib/endianness.h" #include "wimlib/error.h" #include "wimlib/ntfs_3g.h" #include "wimlib/object_id.h" #include "wimlib/paths.h" #include "wimlib/reparse.h" #include "wimlib/scan.h" #include "wimlib/security.h" /* NTFS-3G 2013 renamed MS_RDONLY to NTFS_MNT_RDONLY. We can't check for the * existence of NTFS_MNT_RDONLY at compilation time because it's an enum. We * also can't check for MS_RDONLY being missing because it's also a system * constant. So check if the NTFS-3G specific MS_IGNORE_HIBERFILE is defined; * if yes, then we need to use the old MS_RDONLY. */ #ifdef MS_IGNORE_HIBERFILE # define NTFS_MNT_RDONLY MS_RDONLY #endif /* A reference-counted NTFS volume than is automatically unmounted when the * reference count reaches 0 */ struct ntfs_volume_wrapper { ntfs_volume *vol; size_t refcnt; bool dedup_warned; }; /* Description of where data is located in an NTFS volume */ struct ntfs_location { struct ntfs_volume_wrapper *volume; u64 mft_no; ATTR_TYPES attr_type; u32 attr_name_nchars; ntfschar *attr_name; u64 sort_key; }; static struct ntfs_volume_wrapper * get_ntfs_volume(struct ntfs_volume_wrapper *volume) { volume->refcnt++; return volume; } static void put_ntfs_volume(struct ntfs_volume_wrapper *volume) { if (--volume->refcnt == 0) { ntfs_umount(volume->vol, FALSE); FREE(volume); } } static inline const ntfschar * attr_record_name(const ATTR_RECORD *record) { return (const ntfschar *) ((const u8 *)record + le16_to_cpu(record->name_offset)); } static ntfs_attr * open_ntfs_attr(ntfs_inode *ni, const struct ntfs_location *loc) { ntfs_attr *na; na = ntfs_attr_open(ni, loc->attr_type, loc->attr_name, loc->attr_name_nchars); if (!na) { ERROR_WITH_ERRNO("Failed to open attribute of NTFS inode %"PRIu64, loc->mft_no); } return na; } int read_ntfs_attribute_prefix(const struct blob_descriptor *blob, u64 size, const struct consume_chunk_callback *cb) { const struct ntfs_location *loc = blob->ntfs_loc; ntfs_volume *vol = loc->volume->vol; ntfs_inode *ni; ntfs_attr *na; s64 pos; s64 bytes_remaining; int ret; u8 buf[BUFFER_SIZE]; ni = ntfs_inode_open(vol, loc->mft_no); if (!ni) { ERROR_WITH_ERRNO("Failed to open NTFS inode %"PRIu64, loc->mft_no); ret = WIMLIB_ERR_NTFS_3G; goto out; } na = open_ntfs_attr(ni, loc); if (!na) { ret = WIMLIB_ERR_NTFS_3G; goto out_close_ntfs_inode; } pos = (loc->attr_type == AT_REPARSE_POINT) ? REPARSE_DATA_OFFSET : 0; bytes_remaining = size; while (bytes_remaining) { s64 to_read = min(bytes_remaining, sizeof(buf)); if (ntfs_attr_pread(na, pos, to_read, buf) != to_read) { ERROR_WITH_ERRNO("Error reading data from NTFS inode " "%"PRIu64, loc->mft_no); ret = WIMLIB_ERR_NTFS_3G; goto out_close_ntfs_attr; } pos += to_read; bytes_remaining -= to_read; ret = consume_chunk(cb, buf, to_read); if (ret) goto out_close_ntfs_attr; } ret = 0; out_close_ntfs_attr: ntfs_attr_close(na); out_close_ntfs_inode: ntfs_inode_close(ni); out: return ret; } void free_ntfs_location(struct ntfs_location *loc) { put_ntfs_volume(loc->volume); if (loc->attr_name != AT_UNNAMED) FREE(loc->attr_name); FREE(loc); } struct ntfs_location * clone_ntfs_location(const struct ntfs_location *loc) { struct ntfs_location *new = memdup(loc, sizeof(*loc)); if (!new) goto err0; if (loc->attr_name != AT_UNNAMED) { new->attr_name = utf16le_dup(loc->attr_name); if (!new->attr_name) goto err1; } new->volume = get_ntfs_volume(loc->volume); return new; err1: FREE(new); err0: return NULL; } int cmp_ntfs_locations(const struct ntfs_location *loc1, const struct ntfs_location *loc2) { return cmp_u64(loc1->sort_key, loc2->sort_key); } /* Read rptag and rpreserved from the NTFS inode and save them in the WIM inode. */ static int read_reparse_header(ntfs_inode *ni, struct wim_inode *inode) { struct { le32 rptag; le16 rpdatalen; le16 rpreserved; } hdr; s64 res; ntfs_attr *na; na = ntfs_attr_open(ni, AT_REPARSE_POINT, AT_UNNAMED, 0); if (!na) return WIMLIB_ERR_NTFS_3G; res = ntfs_attr_pread(na, 0, sizeof(hdr), &hdr); ntfs_attr_close(na); if (res != sizeof(hdr)) return WIMLIB_ERR_NTFS_3G; inode->i_reparse_tag = le32_to_cpu(hdr.rptag); inode->i_rp_reserved = le16_to_cpu(hdr.rpreserved); return 0; } static void warn_special_reparse_points(const struct wim_inode *inode, const struct scan_params *params, struct ntfs_volume_wrapper *volume) { if (inode->i_reparse_tag == WIM_IO_REPARSE_TAG_DEDUP && (params->add_flags & WIMLIB_ADD_FLAG_WINCONFIG) && !volume->dedup_warned) { WARNING( "Filesystem includes files deduplicated with Windows'\n" " Data Deduplication feature, which to properly restore\n" " would require that the chunk store in \"System Volume Information\"\n" " be included in the WIM image. By default \"System Volume Information\"\n" " is excluded, so you may want to use a custom capture configuration\n" " file which includes it."); volume->dedup_warned = true; } } static int attr_type_to_wimlib_stream_type(ATTR_TYPES type) { switch (type) { case AT_DATA: return STREAM_TYPE_DATA; case AT_REPARSE_POINT: return STREAM_TYPE_REPARSE_POINT; default: wimlib_assert(0); return STREAM_TYPE_UNKNOWN; } } /* When sorting blobs located in NTFS volumes for sequential reading, we sort * first by starting LCN of the attribute if available, otherwise no sort order * is defined. This usually results in better sequential access to the volume. */ static int set_attr_sort_key(ntfs_inode *ni, struct ntfs_location *loc) { ntfs_attr *na; runlist_element *rl; na = open_ntfs_attr(ni, loc); if (!na) return WIMLIB_ERR_NTFS_3G; rl = ntfs_attr_find_vcn(na, 0); if (rl && rl->lcn != LCN_HOLE) loc->sort_key = rl->lcn; else loc->sort_key = 0; ntfs_attr_close(na); return 0; } /* * Add a new stream to the specified inode, with duplicate checking. * * This works around a problem where NTFS-3G can list multiple unnamed data * streams for a single file. In this case we can only keep one. We'll prefer * one that is nonempty. */ static int add_stream(struct wim_inode *inode, const char *path, int stream_type, const utf16lechar *stream_name, struct blob_descriptor **blob_p, struct list_head *unhashed_blobs) { struct blob_descriptor *blob = *blob_p; struct wim_inode_stream *strm; strm = inode_get_stream(inode, stream_type, stream_name); if (unlikely(strm)) { /* Stream already existed. */ if (!blob) return 0; if (stream_blob_resolved(strm)) { WARNING("\"%s\" has multiple nonempty streams " "with the same type and name! Only the first " "will be saved.", path); return 0; } inode_replace_stream_blob(inode, strm, blob, NULL); } else { strm = inode_add_stream(inode, stream_type, stream_name, blob); if (unlikely(!strm)) return WIMLIB_ERR_NOMEM; } prepare_unhashed_blob(blob, inode, strm->stream_id, unhashed_blobs); *blob_p = NULL; return 0; } /* Save information about an NTFS attribute (stream) to a WIM inode. */ static int scan_ntfs_attr(struct wim_inode *inode, ntfs_inode *ni, const char *path, struct list_head *unhashed_blobs, struct ntfs_volume_wrapper *volume, ATTR_TYPES type, const ATTR_RECORD *record) { u64 data_size = ntfs_get_attribute_value_length(record); const u32 name_nchars = record->name_length; struct blob_descriptor *blob = NULL; utf16lechar *stream_name = (utf16lechar *)NO_STREAM_NAME; int ret; if (unlikely(name_nchars)) { /* Named stream */ stream_name = utf16le_dupz(attr_record_name(record), name_nchars * sizeof(ntfschar)); if (!stream_name) { ret = WIMLIB_ERR_NOMEM; goto out_cleanup; } } if (unlikely(type == AT_REPARSE_POINT)) { if (data_size < REPARSE_DATA_OFFSET) { ERROR("Reparse point attribute of \"%s\" " "is too short!", path); ret = WIMLIB_ERR_INVALID_REPARSE_DATA; goto out_cleanup; } data_size -= REPARSE_DATA_OFFSET; ret = read_reparse_header(ni, inode); if (ret) { ERROR_WITH_ERRNO("Error reading reparse point header " "of \"%s\"", path); goto out_cleanup; } } /* If the stream is non-empty, set up a blob descriptor for it. */ if (data_size != 0) { blob = new_blob_descriptor(); if (unlikely(!blob)) { ret = WIMLIB_ERR_NOMEM; goto out_cleanup; } blob->ntfs_loc = MALLOC(sizeof(struct ntfs_location)); if (unlikely(!blob->ntfs_loc)) { ret = WIMLIB_ERR_NOMEM; goto out_cleanup; } blob->blob_location = BLOB_IN_NTFS_VOLUME; blob->size = data_size; blob->ntfs_loc->volume = get_ntfs_volume(volume); blob->ntfs_loc->mft_no = ni->mft_no; blob->ntfs_loc->attr_type = type; if (unlikely(name_nchars)) { blob->ntfs_loc->attr_name_nchars = name_nchars; blob->ntfs_loc->attr_name = utf16le_dup(stream_name); if (!blob->ntfs_loc->attr_name) { ret = WIMLIB_ERR_NOMEM; goto out_cleanup; } } else { blob->ntfs_loc->attr_name_nchars = 0; blob->ntfs_loc->attr_name = AT_UNNAMED; } ret = set_attr_sort_key(ni, blob->ntfs_loc); if (ret) goto out_cleanup; } ret = add_stream(inode, path, attr_type_to_wimlib_stream_type(type), stream_name, &blob, unhashed_blobs); out_cleanup: free_blob_descriptor(blob); if (stream_name != NO_STREAM_NAME) FREE(stream_name); return ret; } /* Scan attributes of the specified type from a file in the NTFS volume */ static int scan_ntfs_attrs_with_type(struct wim_inode *inode, ntfs_inode *ni, const char *path, struct list_head *unhashed_blobs, struct ntfs_volume_wrapper *volume, ATTR_TYPES type) { ntfs_attr_search_ctx *actx; int ret; actx = ntfs_attr_get_search_ctx(ni, NULL); if (!actx) { ERROR_WITH_ERRNO("Failed to get NTFS attribute search " "context for \"%s\"", path); return WIMLIB_ERR_NTFS_3G; } while (!ntfs_attr_lookup(type, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, actx)) { ret = scan_ntfs_attr(inode, ni, path, unhashed_blobs, volume, type, actx->attr); if (ret) goto out_put_actx; } if (errno != ENOENT) { ERROR_WITH_ERRNO("Error listing NTFS attributes of \"%s\"", path); ret = WIMLIB_ERR_NTFS_3G; goto out_put_actx; } ret = 0; out_put_actx: ntfs_attr_put_search_ctx(actx); return ret; } static noinline_for_stack int load_object_id(ntfs_inode *ni, struct wim_inode *inode) { OBJECT_ID_ATTR attr; int len; len = ntfs_get_ntfs_object_id(ni, (char *)&attr, sizeof(attr)); if (likely(len == -ENODATA || len == 0)) return 0; if (len < 0) return WIMLIB_ERR_NTFS_3G; if (!inode_set_object_id(inode, &attr, len)) return WIMLIB_ERR_NOMEM; return 0; } /* Load the security descriptor of an NTFS inode into the corresponding WIM * inode and the WIM image's security descriptor set. */ static noinline_for_stack int get_security_descriptor(ntfs_inode *ni, struct wim_inode *inode, ntfs_volume *vol, struct wim_sd_set *sd_set) { struct SECURITY_CONTEXT scx = {.vol = vol}; char _buf[4096]; char *buf = _buf; size_t avail_size = sizeof(_buf); int ret; retry: ret = ntfs_get_ntfs_acl(&scx, ni, buf, avail_size); if (unlikely(ret < 0)) { ret = WIMLIB_ERR_NTFS_3G; goto out; } if (unlikely(ret > avail_size)) { if (unlikely(buf != _buf)) FREE(buf); buf = MALLOC(ret); if (!buf) { ret = WIMLIB_ERR_NOMEM; goto out; } avail_size = ret; goto retry; } if (likely(ret > 0)) { inode->i_security_id = sd_set_add_sd(sd_set, buf, ret); if (unlikely(inode->i_security_id < 0)) { ret = WIMLIB_ERR_NOMEM; goto out; } } ret = 0; out: if (unlikely(buf != _buf)) FREE(buf); return ret; } /* Binary tree that maps NTFS inode numbers to DOS names */ struct dos_name_map { struct avl_tree_node *root; }; struct dos_name_node { struct avl_tree_node index_node; char dos_name[24]; int name_nbytes; u64 ntfs_ino; }; #define DOS_NAME_NODE(avl_node) \ avl_tree_entry(avl_node, struct dos_name_node, index_node) static int _avl_cmp_by_ntfs_ino(const struct avl_tree_node *n1, const struct avl_tree_node *n2) { return cmp_u64(DOS_NAME_NODE(n1)->ntfs_ino, DOS_NAME_NODE(n2)->ntfs_ino); } /* Inserts a new DOS name into the map */ static int insert_dos_name(struct dos_name_map *map, const ntfschar *dos_name, size_t name_nbytes, u64 ntfs_ino) { struct dos_name_node *new_node; new_node = MALLOC(sizeof(struct dos_name_node)); if (!new_node) return WIMLIB_ERR_NOMEM; /* DOS names are supposed to be 12 characters max (that's 24 bytes, * assuming 2-byte ntfs characters) */ wimlib_assert(name_nbytes <= sizeof(new_node->dos_name)); /* Initialize the DOS name, DOS name length, and NTFS inode number of * the search tree node */ memcpy(new_node->dos_name, dos_name, name_nbytes); new_node->name_nbytes = name_nbytes; new_node->ntfs_ino = ntfs_ino; /* Insert the search tree node */ if (avl_tree_insert(&map->root, &new_node->index_node, _avl_cmp_by_ntfs_ino)) { /* This should be impossible since an NTFS inode cannot have * multiple DOS names, and we only should get each DOS name * entry once from the ntfs_readdir() calls. */ WARNING("NTFS inode %"PRIu64" has multiple DOS names", ntfs_ino); FREE(new_node); } return 0; } /* Returns a structure that contains the DOS name and its length for an NTFS * inode, or NULL if the inode has no DOS name. */ static struct dos_name_node * lookup_dos_name(const struct dos_name_map *map, u64 ntfs_ino) { struct dos_name_node dummy; struct avl_tree_node *res; dummy.ntfs_ino = ntfs_ino; res = avl_tree_lookup_node(map->root, &dummy.index_node, _avl_cmp_by_ntfs_ino); if (!res) return NULL; return DOS_NAME_NODE(res); } static int set_dentry_dos_name(struct wim_dentry *dentry, const struct dos_name_map *map) { const struct dos_name_node *node; if (dentry->d_is_win32_name) { node = lookup_dos_name(map, dentry->d_inode->i_ino); if (node) { dentry->d_short_name = utf16le_dupz(node->dos_name, node->name_nbytes); if (!dentry->d_short_name) return WIMLIB_ERR_NOMEM; dentry->d_short_name_nbytes = node->name_nbytes; } else { WARNING("NTFS inode %"PRIu64" has Win32 name with no " "corresponding DOS name", dentry->d_inode->i_ino); } } return 0; } static void destroy_dos_name_map(struct dos_name_map *map) { struct dos_name_node *node; avl_tree_for_each_in_postorder(node, map->root, struct dos_name_node, index_node) FREE(node); } struct readdir_ctx { struct wim_dentry *parent; struct dos_name_map dos_name_map; struct ntfs_volume_wrapper *volume; struct scan_params *params; int ret; }; static int ntfs_3g_build_dentry_tree_recursive(struct wim_dentry **root_p, const MFT_REF mref, const char *filename, int name_type, struct ntfs_volume_wrapper *volume, struct scan_params *params); static int filldir(void *_ctx, const ntfschar *name, const int name_nchars, const int name_type, const s64 pos, const MFT_REF mref, const unsigned dt_type) { struct readdir_ctx *ctx = _ctx; struct scan_params *params = ctx->params; const size_t name_nbytes = name_nchars * sizeof(ntfschar); char *mbs_name; size_t mbs_name_nbytes; size_t orig_path_nchars; struct wim_dentry *child; int ret; if (name_type & FILE_NAME_DOS) { /* If this is the entry for a DOS name, store it for later. */ ret = insert_dos_name(&ctx->dos_name_map, name, name_nbytes, MREF(mref)); /* Return now if an error occurred or if this is just a DOS name * and not a Win32+DOS name. */ if (ret != 0 || name_type == FILE_NAME_DOS) goto out; } ret = utf16le_to_tstr(name, name_nbytes, &mbs_name, &mbs_name_nbytes); if (ret) goto out; if (should_ignore_filename(mbs_name, mbs_name_nbytes)) goto out_free_mbs_name; ret = WIMLIB_ERR_NOMEM; if (!pathbuf_append_name(params, mbs_name, mbs_name_nbytes, &orig_path_nchars)) goto out_free_mbs_name; child = NULL; ret = ntfs_3g_build_dentry_tree_recursive(&child, mref, mbs_name, name_type, ctx->volume, params); pathbuf_truncate(params, orig_path_nchars); attach_scanned_tree(ctx->parent, child, params->blob_table); out_free_mbs_name: FREE(mbs_name); out: ctx->ret = ret; return ret; } static int ntfs_3g_recurse_directory(ntfs_inode *ni, struct wim_dentry *parent, struct ntfs_volume_wrapper *volume, struct scan_params *params) { int ret; s64 pos = 0; struct readdir_ctx ctx = { .parent = parent, .dos_name_map = { .root = NULL }, .volume = volume, .params = params, .ret = 0, }; ret = ntfs_readdir(ni, &pos, &ctx, filldir); if (unlikely(ret)) { if (ctx.ret) { /* wimlib error */ ret = ctx.ret; } else { /* error from ntfs_readdir() itself */ ERROR_WITH_ERRNO("Error reading directory \"%s\"", params->cur_path); ret = WIMLIB_ERR_NTFS_3G; } } else { struct wim_dentry *child; ret = 0; for_dentry_child(child, parent) { ret = set_dentry_dos_name(child, &ctx.dos_name_map); if (ret) break; } } destroy_dos_name_map(&ctx.dos_name_map); return ret; } static int ntfs_3g_build_dentry_tree_recursive(struct wim_dentry **root_ret, const MFT_REF mref, const char *filename, int name_type, struct ntfs_volume_wrapper *volume, struct scan_params *params) { const char *path = params->cur_path; u32 attributes; int ret; struct wim_dentry *root = NULL; struct wim_inode *inode = NULL; ntfs_inode *ni = NULL; ret = try_exclude(params); if (unlikely(ret < 0)) /* Excluded? */ goto out_progress; if (unlikely(ret > 0)) /* Error? */ goto out; ni = ntfs_inode_open(volume->vol, mref); if (!ni) { ERROR_WITH_ERRNO("Failed to open NTFS file \"%s\"", path); ret = WIMLIB_ERR_NTFS_3G; goto out; } /* Get file attributes */ ret = ntfs_get_ntfs_attrib(ni, (char*)&attributes, sizeof(attributes)); if (ret != sizeof(attributes)) { ERROR_WITH_ERRNO("Failed to get NTFS attributes from \"%s\"", path); ret = WIMLIB_ERR_NTFS_3G; goto out; } if (unlikely(attributes & FILE_ATTRIBUTE_ENCRYPTED)) { if (params->add_flags & WIMLIB_ADD_FLAG_NO_UNSUPPORTED_EXCLUDE) { ERROR("Can't archive \"%s\" because NTFS-3G capture mode " "does not support encrypted files and directories", path); ret = WIMLIB_ERR_UNSUPPORTED_FILE; goto out; } ret = do_scan_progress(params, WIMLIB_SCAN_DENTRY_UNSUPPORTED, NULL); goto out; } /* Create a WIM dentry with an associated inode, which may be shared */ ret = inode_table_new_dentry(params->inode_table, filename, ni->mft_no, 0, false, &root); if (ret) goto out; if (name_type & FILE_NAME_WIN32) /* Win32 or Win32+DOS name (rather than POSIX) */ root->d_is_win32_name = 1; inode = root->d_inode; if (inode->i_nlink > 1) { /* Shared inode; nothing more to do */ goto out_progress; } inode->i_creation_time = le64_to_cpu(ni->creation_time); inode->i_last_write_time = le64_to_cpu(ni->last_data_change_time); inode->i_last_access_time = le64_to_cpu(ni->last_access_time); inode->i_attributes = attributes; if (attributes & FILE_ATTRIBUTE_REPARSE_POINT) { /* Scan the reparse point stream. */ ret = scan_ntfs_attrs_with_type(inode, ni, path, params->unhashed_blobs, volume, AT_REPARSE_POINT); if (ret) goto out; warn_special_reparse_points(inode, params, volume); } /* Load the object ID. */ ret = load_object_id(ni, inode); if (ret) { ERROR_WITH_ERRNO("Error reading object ID of \"%s\"", path); goto out; } /* Scan the data streams. * * Note: directories should not have an unnamed data stream, but they * may have named data streams. Nondirectories (including reparse * points) can have an unnamed data stream as well as named data * streams. */ ret = scan_ntfs_attrs_with_type(inode, ni, path, params->unhashed_blobs, volume, AT_DATA); if (ret) goto out; /* Reparse-point fixups are a no-op because in NTFS-3G capture mode we * only allow capturing an entire volume. */ if (params->add_flags & WIMLIB_ADD_FLAG_RPFIX && inode_is_symlink(inode)) inode->i_rp_flags &= ~WIM_RP_FLAG_NOT_FIXED; if (!(params->add_flags & WIMLIB_ADD_FLAG_NO_ACLS)) { ret = get_security_descriptor(ni, inode, volume->vol, params->sd_set); if (ret) { ERROR_WITH_ERRNO("Error reading security descriptor " "of \"%s\"", path); goto out; } } if (inode_is_directory(inode)) { ret = ntfs_3g_recurse_directory(ni, root, volume, params); if (ret) goto out; } out_progress: if (root == NULL) ret = do_scan_progress(params, WIMLIB_SCAN_DENTRY_EXCLUDED, NULL); else ret = do_scan_progress(params, WIMLIB_SCAN_DENTRY_OK, inode); out: if (ni) ntfs_inode_close(ni); if (unlikely(ret)) { free_dentry_tree(root, params->blob_table); root = NULL; ret = report_scan_error(params, ret); } *root_ret = root; return ret; } int ntfs_3g_build_dentry_tree(struct wim_dentry **root_ret, const char *device, struct scan_params *params) { struct ntfs_volume_wrapper *volume; ntfs_volume *vol; int ret; volume = CALLOC(1, sizeof(struct ntfs_volume_wrapper)); if (!volume) return WIMLIB_ERR_NOMEM; vol = ntfs_mount(device, NTFS_MNT_RDONLY); if (!vol) { ERROR_WITH_ERRNO("Failed to mount NTFS volume \"%s\" read-only", device); FREE(volume); return WIMLIB_ERR_NTFS_3G; } volume->vol = vol; volume->refcnt = 1; /* Currently, libntfs-3g users that need to read security descriptors * are required to call ntfs_open_secure() to open the volume's security * descriptor index, "$Secure". This is only required to work on NTFS * v3.0+, as older versions did not have a security descriptor index. */ if (ntfs_open_secure(vol) && vol->major_ver >= 3) { ERROR_WITH_ERRNO("Unable to open security descriptor index of " "NTFS volume \"%s\"", device); ret = WIMLIB_ERR_NTFS_3G; goto out_put_ntfs_volume; } /* We don't want to capture the special NTFS files such as $Bitmap. Not * to be confused with "hidden" or "system" files which are real files * that we do need to capture. */ NVolClearShowSysFiles(vol); ret = pathbuf_init(params, "/"); if (ret) goto out_close_secure; ret = ntfs_3g_build_dentry_tree_recursive(root_ret, FILE_root, "", FILE_NAME_POSIX, volume, params); out_close_secure: /* Undo the effects of ntfs_open_secure(). This is not yet done * automatically by ntfs_umount(). But NULL out the inode to * potentially be robust against future versions doing so. */ if (vol->secure_ni) { ntfs_index_ctx_put(vol->secure_xsii); ntfs_index_ctx_put(vol->secure_xsdh); ntfs_inode_close(vol->secure_ni); vol->secure_ni = NULL; } out_put_ntfs_volume: put_ntfs_volume(volume); return ret; } #endif /* WITH_NTFS_3G */ wimlib-1.13.1/src/header.c0000644000175000017500000002250313277475672012212 00000000000000/* * header.c * * Read, write, or print a WIM header. */ /* * Copyright (C) 2012, 2013, 2015 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include "wimlib.h" #include "wimlib/alloca.h" #include "wimlib/assert.h" #include "wimlib/endianness.h" #include "wimlib/error.h" #include "wimlib/file_io.h" #include "wimlib/header.h" #include "wimlib/util.h" #include "wimlib/wim.h" /* * Reads the header from a WIM file. * * @wim * WIM to read the header from. @wim->in_fd must be positioned at the * beginning of the file. * * @hdr * Structure to read the header into. * * Return values: * WIMLIB_ERR_SUCCESS (0) * WIMLIB_ERR_IMAGE_COUNT * WIMLIB_ERR_INVALID_PART_NUMBER * WIMLIB_ERR_NOT_A_WIM_FILE * WIMLIB_ERR_READ * WIMLIB_ERR_UNEXPECTED_END_OF_FILE * WIMLIB_ERR_UNKNOWN_VERSION */ int read_wim_header(WIMStruct *wim, struct wim_header *hdr) { struct wim_header_disk disk_hdr _aligned_attribute(8); struct filedes *in_fd = &wim->in_fd; const tchar *filename = wim->filename; int ret; tchar *pipe_str; wimlib_assert(in_fd->offset == 0); if (filename == NULL) { pipe_str = alloca(40); tsprintf(pipe_str, T("[fd %d]"), in_fd->fd); filename = pipe_str; } STATIC_ASSERT(sizeof(struct wim_header_disk) == WIM_HEADER_DISK_SIZE); ret = full_read(in_fd, &disk_hdr, sizeof(disk_hdr)); if (ret) goto read_error; hdr->magic = le64_to_cpu(disk_hdr.magic); if (hdr->magic != WIM_MAGIC) { if (hdr->magic == PWM_MAGIC) { /* Pipable WIM: Use header at end instead, unless * actually reading from a pipe. */ if (!in_fd->is_pipe) { ret = WIMLIB_ERR_READ; if (-1 == lseek(in_fd->fd, -WIM_HEADER_DISK_SIZE, SEEK_END)) goto read_error; ret = full_read(in_fd, &disk_hdr, sizeof(disk_hdr)); if (ret) goto read_error; } } else { ERROR("\"%"TS"\": Invalid magic characters in header", filename); return WIMLIB_ERR_NOT_A_WIM_FILE; } } if (le32_to_cpu(disk_hdr.hdr_size) != sizeof(struct wim_header_disk)) { ERROR("\"%"TS"\": Header size is invalid (%u bytes)", filename, le32_to_cpu(disk_hdr.hdr_size)); return WIMLIB_ERR_INVALID_HEADER; } hdr->wim_version = le32_to_cpu(disk_hdr.wim_version); if (hdr->wim_version != WIM_VERSION_DEFAULT && hdr->wim_version != WIM_VERSION_SOLID) { ERROR("\"%"TS"\": Unknown WIM version: %u", filename, hdr->wim_version); return WIMLIB_ERR_UNKNOWN_VERSION; } hdr->flags = le32_to_cpu(disk_hdr.wim_flags); hdr->chunk_size = le32_to_cpu(disk_hdr.chunk_size); copy_guid(hdr->guid, disk_hdr.guid); hdr->part_number = le16_to_cpu(disk_hdr.part_number); hdr->total_parts = le16_to_cpu(disk_hdr.total_parts); if (hdr->total_parts == 0 || hdr->part_number == 0 || hdr->part_number > hdr->total_parts) { ERROR("\"%"TS"\": Invalid WIM part number: %hu of %hu", filename, hdr->part_number, hdr->total_parts); return WIMLIB_ERR_INVALID_PART_NUMBER; } hdr->image_count = le32_to_cpu(disk_hdr.image_count); if (unlikely(hdr->image_count > MAX_IMAGES)) { ERROR("\"%"TS"\": Invalid image count (%u)", filename, hdr->image_count); return WIMLIB_ERR_IMAGE_COUNT; } get_wim_reshdr(&disk_hdr.blob_table_reshdr, &hdr->blob_table_reshdr); get_wim_reshdr(&disk_hdr.xml_data_reshdr, &hdr->xml_data_reshdr); get_wim_reshdr(&disk_hdr.boot_metadata_reshdr, &hdr->boot_metadata_reshdr); hdr->boot_idx = le32_to_cpu(disk_hdr.boot_idx); get_wim_reshdr(&disk_hdr.integrity_table_reshdr, &hdr->integrity_table_reshdr); return 0; read_error: ERROR_WITH_ERRNO("\"%"TS"\": Error reading header", filename); return ret; } /* Writes the header for a WIM file at the specified offset. If the offset * specified is the current one, the position is advanced by the size of the * header. */ int write_wim_header(const struct wim_header *hdr, struct filedes *out_fd, off_t offset) { struct wim_header_disk disk_hdr _aligned_attribute(8); int ret; disk_hdr.magic = cpu_to_le64(hdr->magic); disk_hdr.hdr_size = cpu_to_le32(sizeof(struct wim_header_disk)); disk_hdr.wim_version = cpu_to_le32(hdr->wim_version); disk_hdr.wim_flags = cpu_to_le32(hdr->flags); disk_hdr.chunk_size = cpu_to_le32(hdr->chunk_size); copy_guid(disk_hdr.guid, hdr->guid); disk_hdr.part_number = cpu_to_le16(hdr->part_number); disk_hdr.total_parts = cpu_to_le16(hdr->total_parts); disk_hdr.image_count = cpu_to_le32(hdr->image_count); put_wim_reshdr(&hdr->blob_table_reshdr, &disk_hdr.blob_table_reshdr); put_wim_reshdr(&hdr->xml_data_reshdr, &disk_hdr.xml_data_reshdr); put_wim_reshdr(&hdr->boot_metadata_reshdr, &disk_hdr.boot_metadata_reshdr); disk_hdr.boot_idx = cpu_to_le32(hdr->boot_idx); put_wim_reshdr(&hdr->integrity_table_reshdr, &disk_hdr.integrity_table_reshdr); memset(disk_hdr.unused, 0, sizeof(disk_hdr.unused)); if (offset == out_fd->offset) ret = full_write(out_fd, &disk_hdr, sizeof(disk_hdr)); else ret = full_pwrite(out_fd, &disk_hdr, sizeof(disk_hdr), offset); if (ret) ERROR_WITH_ERRNO("Failed to write WIM header"); return ret; } /* Update just the wim_flags field. */ int write_wim_header_flags(u32 hdr_flags, struct filedes *out_fd) { le32 flags = cpu_to_le32(hdr_flags); return full_pwrite(out_fd, &flags, sizeof(flags), offsetof(struct wim_header_disk, wim_flags)); } static const struct { u32 flag; const char *name; } hdr_flags[] = { {WIM_HDR_FLAG_RESERVED, "RESERVED"}, {WIM_HDR_FLAG_COMPRESSION, "COMPRESSION"}, {WIM_HDR_FLAG_READONLY, "READONLY"}, {WIM_HDR_FLAG_SPANNED, "SPANNED"}, {WIM_HDR_FLAG_RESOURCE_ONLY, "RESOURCE_ONLY"}, {WIM_HDR_FLAG_METADATA_ONLY, "METADATA_ONLY"}, {WIM_HDR_FLAG_WRITE_IN_PROGRESS,"WRITE_IN_PROGRESS"}, {WIM_HDR_FLAG_RP_FIX, "RP_FIX"}, {WIM_HDR_FLAG_COMPRESS_RESERVED,"COMPRESS_RESERVED"}, {WIM_HDR_FLAG_COMPRESS_LZX, "COMPRESS_LZX"}, {WIM_HDR_FLAG_COMPRESS_XPRESS, "COMPRESS_XPRESS"}, {WIM_HDR_FLAG_COMPRESS_LZMS, "COMPRESS_LZMS"}, {WIM_HDR_FLAG_COMPRESS_XPRESS_2,"COMPRESS_XPRESS_2"}, }; /* API function documented in wimlib.h */ WIMLIBAPI void wimlib_print_header(const WIMStruct *wim) { const struct wim_header *hdr = &wim->hdr; tprintf(T("Magic Characters = ")); for (int i = 0; i < sizeof(hdr->magic); i++) { tchar c = (u8)(hdr->magic >> ((8 * i))); if (istalpha(c)) tputchar(c); else tprintf(T("\\%o"), c); } tputchar(T('\n')); tprintf(T("Header Size = %u\n"), WIM_HEADER_DISK_SIZE); tprintf(T("Version = 0x%x\n"), hdr->wim_version); tprintf(T("Flags = 0x%x\n"), hdr->flags); for (size_t i = 0; i < ARRAY_LEN(hdr_flags); i++) if (hdr_flags[i].flag & hdr->flags) tprintf(T(" WIM_HDR_FLAG_%s is set\n"), hdr_flags[i].name); tprintf(T("Chunk Size = %u\n"), hdr->chunk_size); tfputs (T("GUID = "), stdout); print_byte_field(hdr->guid, GUID_SIZE, stdout); tputchar(T('\n')); tprintf(T("Part Number = %hu\n"), hdr->part_number); tprintf(T("Total Parts = %hu\n"), hdr->total_parts); tprintf(T("Image Count = %u\n"), hdr->image_count); tprintf(T("Blob Table Size = %"PRIu64"\n"), (u64)hdr->blob_table_reshdr.size_in_wim); tprintf(T("Blob Table Flags = 0x%hhx\n"), (u8)hdr->blob_table_reshdr.flags); tprintf(T("Blob Table Offset = %"PRIu64"\n"), hdr->blob_table_reshdr.offset_in_wim); tprintf(T("Blob Table Original_size = %"PRIu64"\n"), hdr->blob_table_reshdr.uncompressed_size); tprintf(T("XML Data Size = %"PRIu64"\n"), (u64)hdr->xml_data_reshdr.size_in_wim); tprintf(T("XML Data Flags = 0x%hhx\n"), (u8)hdr->xml_data_reshdr.flags); tprintf(T("XML Data Offset = %"PRIu64"\n"), hdr->xml_data_reshdr.offset_in_wim); tprintf(T("XML Data Original Size = %"PRIu64"\n"), hdr->xml_data_reshdr.uncompressed_size); tprintf(T("Boot Metadata Size = %"PRIu64"\n"), (u64)hdr->boot_metadata_reshdr.size_in_wim); tprintf(T("Boot Metadata Flags = 0x%hhx\n"), (u8)hdr->boot_metadata_reshdr.flags); tprintf(T("Boot Metadata Offset = %"PRIu64"\n"), hdr->boot_metadata_reshdr.offset_in_wim); tprintf(T("Boot Metadata Original Size = %"PRIu64"\n"), hdr->boot_metadata_reshdr.uncompressed_size); tprintf(T("Boot Index = %u\n"), hdr->boot_idx); tprintf(T("Integrity Size = %"PRIu64"\n"), (u64)hdr->integrity_table_reshdr.size_in_wim); tprintf(T("Integrity Flags = 0x%hhx\n"), (u8)hdr->integrity_table_reshdr.flags); tprintf(T("Integrity Offset = %"PRIu64"\n"), hdr->integrity_table_reshdr.offset_in_wim); tprintf(T("Integrity Original_size = %"PRIu64"\n"), hdr->integrity_table_reshdr.uncompressed_size); } wimlib-1.13.1/src/decompress.c0000644000175000017500000000541113277475672013125 00000000000000/* * decompress.c * * Generic functions for decompression, wrapping around actual decompression * implementations. */ /* * Copyright (C) 2013, 2014 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib.h" #include "wimlib/decompressor_ops.h" #include "wimlib/util.h" struct wimlib_decompressor { const struct decompressor_ops *ops; size_t max_block_size; void *private; }; static const struct decompressor_ops * const decompressor_ops[] = { [WIMLIB_COMPRESSION_TYPE_XPRESS] = &xpress_decompressor_ops, [WIMLIB_COMPRESSION_TYPE_LZX] = &lzx_decompressor_ops, [WIMLIB_COMPRESSION_TYPE_LZMS] = &lzms_decompressor_ops, }; static bool decompressor_ctype_valid(int ctype) { return (ctype >= 0 && ctype < ARRAY_LEN(decompressor_ops) && decompressor_ops[ctype] != NULL); } WIMLIBAPI int wimlib_create_decompressor(enum wimlib_compression_type ctype, size_t max_block_size, struct wimlib_decompressor **dec_ret) { struct wimlib_decompressor *dec; if (!decompressor_ctype_valid(ctype)) return WIMLIB_ERR_INVALID_COMPRESSION_TYPE; if (dec_ret == NULL) return WIMLIB_ERR_INVALID_PARAM; if (max_block_size == 0) return WIMLIB_ERR_INVALID_PARAM; dec = MALLOC(sizeof(*dec)); if (dec == NULL) return WIMLIB_ERR_NOMEM; dec->ops = decompressor_ops[ctype]; dec->max_block_size = max_block_size; dec->private = NULL; if (dec->ops->create_decompressor) { int ret; ret = dec->ops->create_decompressor(max_block_size, &dec->private); if (ret) { FREE(dec); return ret; } } *dec_ret = dec; return 0; } WIMLIBAPI int wimlib_decompress(const void *compressed_data, size_t compressed_size, void *uncompressed_data, size_t uncompressed_size, struct wimlib_decompressor *dec) { if (unlikely(uncompressed_size > dec->max_block_size)) return -2; return dec->ops->decompress(compressed_data, compressed_size, uncompressed_data, uncompressed_size, dec->private); } WIMLIBAPI void wimlib_free_decompressor(struct wimlib_decompressor *dec) { if (dec) { if (dec->ops->free_decompressor) dec->ops->free_decompressor(dec->private); FREE(dec); } } wimlib-1.13.1/src/error.c0000644000175000017500000002644713160354224012102 00000000000000/* * error.c - logging and error code translation */ /* * Copyright (C) 2012, 2013, 2014 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif /* Make sure the POSIX-compatible strerror_r() is declared, rather than the GNU * version, which has a different return type. */ #ifdef _GNU_SOURCE # define GNU_SOURCE_WAS_DEFINED # undef _GNU_SOURCE # ifndef _POSIX_C_SOURCE # define _POSIX_C_SOURCE 200112L # endif #endif #include #ifdef GNU_SOURCE_WAS_DEFINED # define _GNU_SOURCE #endif #include #include #include "wimlib.h" #include "wimlib/error.h" #include "wimlib/test_support.h" #include "wimlib/util.h" #include "wimlib/win32.h" #ifdef ENABLE_ERROR_MESSAGES bool wimlib_print_errors = false; FILE *wimlib_error_file = NULL; /* Set in wimlib_global_init() */ static bool wimlib_owns_error_file = false; static void wimlib_vmsg(const tchar *tag, const tchar *format, va_list va, bool perror) { if (!wimlib_print_errors) return; int errno_save = errno; fflush(stdout); tfputs(tag, wimlib_error_file); tvfprintf(wimlib_error_file, format, va); if (perror && errno_save != 0) { tchar buf[64]; int res; res = tstrerror_r(errno_save, buf, ARRAY_LEN(buf)); if (res) { tsprintf(buf, T("unknown error (errno=%d)"), errno_save); } #ifdef WIN32 if (errno_save == EBUSY) tstrcpy(buf, T("Resource busy")); #endif tfprintf(wimlib_error_file, T(": %"TS), buf); } tputc(T('\n'), wimlib_error_file); fflush(wimlib_error_file); errno = errno_save; } void wimlib_error(const tchar *format, ...) { va_list va; va_start(va, format); wimlib_vmsg(T("\r[ERROR] "), format, va, false); va_end(va); } void wimlib_error_with_errno(const tchar *format, ...) { va_list va; va_start(va, format); wimlib_vmsg(T("\r[ERROR] "), format, va, true); va_end(va); } void wimlib_warning(const tchar *format, ...) { va_list va; va_start(va, format); wimlib_vmsg(T("\r[WARNING] "), format, va, false); va_end(va); } void wimlib_warning_with_errno(const tchar *format, ...) { va_list va; va_start(va, format); wimlib_vmsg(T("\r[WARNING] "), format, va, true); va_end(va); } #endif /* ENABLE_ERROR_MESSAGES */ void print_byte_field(const u8 *field, size_t len, FILE *out) { while (len--) tfprintf(out, T("%02hhx"), *field++); } WIMLIBAPI int wimlib_set_print_errors(bool show_error_messages) { #ifdef ENABLE_ERROR_MESSAGES wimlib_print_errors = show_error_messages; #else if (show_error_messages) return WIMLIB_ERR_UNSUPPORTED; #endif return 0; } WIMLIBAPI int wimlib_set_error_file(FILE *fp) { #ifdef ENABLE_ERROR_MESSAGES if (wimlib_owns_error_file) fclose(wimlib_error_file); wimlib_error_file = fp; wimlib_print_errors = (fp != NULL); wimlib_owns_error_file = false; return 0; #else return WIMLIB_ERR_UNSUPPORTED; #endif } WIMLIBAPI int wimlib_set_error_file_by_name(const tchar *path) { #ifdef ENABLE_ERROR_MESSAGES FILE *fp; #ifdef __WIN32__ fp = win32_open_logfile(path); #else fp = fopen(path, "a"); #endif if (!fp) return WIMLIB_ERR_OPEN; wimlib_set_error_file(fp); wimlib_owns_error_file = true; return 0; #else return WIMLIB_ERR_UNSUPPORTED; #endif } static const tchar * const error_strings[] = { [WIMLIB_ERR_SUCCESS] = T("Success"), [WIMLIB_ERR_ALREADY_LOCKED] = T("The WIM is already locked for writing"), [WIMLIB_ERR_DECOMPRESSION] = T("The WIM contains invalid compressed data"), [WIMLIB_ERR_FUSE] = T("An error was returned by fuse_main()"), [WIMLIB_ERR_GLOB_HAD_NO_MATCHES] = T("The provided file glob did not match any files"), [WIMLIB_ERR_IMAGE_COUNT] = T("Inconsistent image count among the metadata " "resources, the WIM header, and/or the XML data"), [WIMLIB_ERR_IMAGE_NAME_COLLISION] = T("Tried to add an image with a name that is already in use"), [WIMLIB_ERR_INSUFFICIENT_PRIVILEGES] = T("The user does not have sufficient privileges"), [WIMLIB_ERR_INTEGRITY] = T("The WIM file is corrupted (failed integrity check)"), [WIMLIB_ERR_INVALID_CAPTURE_CONFIG] = T("The contents of the capture configuration file were invalid"), [WIMLIB_ERR_INVALID_CHUNK_SIZE] = T("The compression chunk size was unrecognized"), [WIMLIB_ERR_INVALID_COMPRESSION_TYPE] = T("The compression type was unrecognized"), [WIMLIB_ERR_INVALID_HEADER] = T("The WIM header was invalid"), [WIMLIB_ERR_INVALID_IMAGE] = T("Tried to select an image that does not exist in the WIM"), [WIMLIB_ERR_INVALID_INTEGRITY_TABLE] = T("The WIM's integrity table is invalid"), [WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY] = T("An entry in the WIM's lookup table is invalid"), [WIMLIB_ERR_INVALID_METADATA_RESOURCE] = T("The metadata resource is invalid"), [WIMLIB_ERR_INVALID_OVERLAY] = T("Conflicting files in overlay when creating a WIM image"), [WIMLIB_ERR_INVALID_PARAM] = T("An invalid parameter was given"), [WIMLIB_ERR_INVALID_PART_NUMBER] = T("The part number or total parts of the WIM is invalid"), [WIMLIB_ERR_INVALID_PIPABLE_WIM] = T("The pipable WIM is invalid"), [WIMLIB_ERR_INVALID_REPARSE_DATA] = T("The reparse data of a reparse point was invalid"), [WIMLIB_ERR_INVALID_RESOURCE_HASH] = T("The SHA-1 message digest of a WIM resource did not match the expected value"), [WIMLIB_ERR_INVALID_UTF8_STRING] = T("A string was not a valid UTF-8 string"), [WIMLIB_ERR_INVALID_UTF16_STRING] = T("A string was not a valid UTF-16 string"), [WIMLIB_ERR_IS_DIRECTORY] = T("One of the specified paths to delete was a directory"), [WIMLIB_ERR_IS_SPLIT_WIM] = T("The WIM is part of a split WIM, which is not supported for this operation"), [WIMLIB_ERR_LINK] = T("Failed to create a hard or symbolic link when extracting " "a file from the WIM"), [WIMLIB_ERR_METADATA_NOT_FOUND] = T("The WIM does not contain image metadata; it only contains file data"), [WIMLIB_ERR_MKDIR] = T("Failed to create a directory"), [WIMLIB_ERR_MQUEUE] = T("Failed to create or use a POSIX message queue"), [WIMLIB_ERR_NOMEM] = T("Ran out of memory"), [WIMLIB_ERR_NOTDIR] = T("Expected a directory"), [WIMLIB_ERR_NOTEMPTY] = T("Directory was not empty"), [WIMLIB_ERR_NOT_A_REGULAR_FILE] = T("One of the specified paths to extract did not " "correspond to a regular file"), [WIMLIB_ERR_NOT_A_WIM_FILE] = T("The file did not begin with the magic characters that " "identify a WIM file"), [WIMLIB_ERR_NO_FILENAME] = T("The WIM is not identified with a filename"), [WIMLIB_ERR_NOT_PIPABLE] = T("The WIM was not captured such that it can be " "applied from a pipe"), [WIMLIB_ERR_NTFS_3G] = T("NTFS-3G encountered an error (check errno)"), [WIMLIB_ERR_OPEN] = T("Failed to open a file"), [WIMLIB_ERR_OPENDIR] = T("Failed to open a directory"), [WIMLIB_ERR_PATH_DOES_NOT_EXIST] = T("The path does not exist in the WIM image"), [WIMLIB_ERR_READ] = T("Could not read data from a file"), [WIMLIB_ERR_READLINK] = T("Could not read the target of a symbolic link"), [WIMLIB_ERR_RENAME] = T("Could not rename a file"), [WIMLIB_ERR_REPARSE_POINT_FIXUP_FAILED] = T("Unable to complete reparse point fixup"), [WIMLIB_ERR_RESOURCE_NOT_FOUND] = T("A file resource needed to complete the operation was missing from the WIM"), [WIMLIB_ERR_RESOURCE_ORDER] = T("The components of the WIM were arranged in an unexpected order"), [WIMLIB_ERR_SET_ATTRIBUTES] = T("Failed to set attributes on extracted file"), [WIMLIB_ERR_SET_REPARSE_DATA] = T("Failed to set reparse data on extracted file"), [WIMLIB_ERR_SET_SECURITY] = T("Failed to set file owner, group, or other permissions on extracted file"), [WIMLIB_ERR_SET_SHORT_NAME] = T("Failed to set short name on extracted file"), [WIMLIB_ERR_SET_TIMESTAMPS] = T("Failed to set timestamps on extracted file"), [WIMLIB_ERR_SPLIT_INVALID] = T("The WIM is part of an invalid split WIM"), [WIMLIB_ERR_STAT] = T("Could not read the metadata for a file or directory"), [WIMLIB_ERR_UNEXPECTED_END_OF_FILE] = T("Unexpectedly reached the end of the file"), [WIMLIB_ERR_UNICODE_STRING_NOT_REPRESENTABLE] = T("A Unicode string could not be represented in the current locale's encoding"), [WIMLIB_ERR_UNKNOWN_VERSION] = T("The WIM file is marked with an unknown version number"), [WIMLIB_ERR_UNSUPPORTED] = T("The requested operation is unsupported"), [WIMLIB_ERR_UNSUPPORTED_FILE] = T("A file in the directory tree to archive was not of a supported type"), [WIMLIB_ERR_WIM_IS_READONLY] = T("The WIM is read-only (file permissions, header flag, or split WIM)"), [WIMLIB_ERR_WRITE] = T("Failed to write data to a file"), [WIMLIB_ERR_XML] = T("The XML data of the WIM is invalid"), [WIMLIB_ERR_WIM_IS_ENCRYPTED] = T("The WIM file (or parts of it) is encrypted"), [WIMLIB_ERR_WIMBOOT] = T("Failed to set WIMBoot pointer data"), [WIMLIB_ERR_ABORTED_BY_PROGRESS] = T("The operation was aborted by the library user"), [WIMLIB_ERR_UNKNOWN_PROGRESS_STATUS] = T("The user-provided progress function returned an unrecognized value"), [WIMLIB_ERR_MKNOD] = T("Unable to create a special file (e.g. device node or socket)"), [WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY] = T("There are still files open on the mounted WIM image"), [WIMLIB_ERR_NOT_A_MOUNTPOINT] = T("There is not a WIM image mounted on the directory"), [WIMLIB_ERR_NOT_PERMITTED_TO_UNMOUNT] = T("The current user does not have permission to unmount the WIM image"), [WIMLIB_ERR_FVE_LOCKED_VOLUME] = T("The volume must be unlocked before it can be used"), [WIMLIB_ERR_UNABLE_TO_READ_CAPTURE_CONFIG] = T("The capture configuration file could not be read"), [WIMLIB_ERR_WIM_IS_INCOMPLETE] = T("The WIM file is incomplete"), [WIMLIB_ERR_COMPACTION_NOT_POSSIBLE] = T("The WIM file cannot be compacted because of its format, " "its layout, or the write parameters specified by the user"), [WIMLIB_ERR_IMAGE_HAS_MULTIPLE_REFERENCES] = T("The WIM image cannot be modified because it is currently " "referenced from multiple places"), [WIMLIB_ERR_DUPLICATE_EXPORTED_IMAGE] = T("The destination WIM already contains one of the source images"), [WIMLIB_ERR_CONCURRENT_MODIFICATION_DETECTED] = T("A file being added to a WIM image was concurrently modified"), [WIMLIB_ERR_SNAPSHOT_FAILURE] = T("Unable to create a filesystem snapshot"), [WIMLIB_ERR_INVALID_XATTR] = T("An extended attribute entry in the WIM image is invalid"), [WIMLIB_ERR_SET_XATTR] = T("Failed to set an extended attribute on an extracted file"), #ifdef ENABLE_TEST_SUPPORT [WIMLIB_ERR_IMAGES_ARE_DIFFERENT] = T("A difference was detected between the two images being compared"), #endif }; WIMLIBAPI const tchar * wimlib_get_error_string(enum wimlib_error_code _code) { unsigned int code = (unsigned int)_code; if (code >= ARRAY_LEN(error_strings) || error_strings[code] == NULL) return T("Unknown error"); return error_strings[code]; } wimlib-1.13.1/src/win32_common.c0000644000175000017500000002314513272231267013260 00000000000000/* * win32_common.c - Windows code common to applying and capturing images. */ /* * Copyright (C) 2013-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef __WIN32__ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib/win32_common.h" #include "wimlib/assert.h" #include "wimlib/error.h" #include "wimlib/util.h" #include "wimlib/win32_vss.h" static bool win32_modify_privilege(const wchar_t *privilege, bool enable) { HANDLE hToken; LUID luid; TOKEN_PRIVILEGES newState; bool ret = FALSE; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) goto out; if (!LookupPrivilegeValue(NULL, privilege, &luid)) goto out_close_handle; newState.PrivilegeCount = 1; newState.Privileges[0].Luid = luid; newState.Privileges[0].Attributes = (enable ? SE_PRIVILEGE_ENABLED : 0); SetLastError(ERROR_SUCCESS); ret = AdjustTokenPrivileges(hToken, FALSE, &newState, 0, NULL, NULL); if (ret && GetLastError() == ERROR_NOT_ALL_ASSIGNED) ret = FALSE; out_close_handle: CloseHandle(hToken); out: return ret; } static bool win32_modify_capture_privileges(bool enable) { bool ok = true; ok &= win32_modify_privilege(SE_BACKUP_NAME, enable); ok &= win32_modify_privilege(SE_SECURITY_NAME, enable); return ok; } static bool win32_modify_apply_privileges(bool enable) { bool ok = true; ok &= win32_modify_privilege(SE_RESTORE_NAME, enable); ok &= win32_modify_privilege(SE_SECURITY_NAME, enable); ok &= win32_modify_privilege(SE_TAKE_OWNERSHIP_NAME, enable); ok &= win32_modify_privilege(SE_MANAGE_VOLUME_NAME, enable); return ok; } static void win32_release_capture_and_apply_privileges(void) { win32_modify_capture_privileges(false); win32_modify_apply_privileges(false); } /* Pointers to dynamically loaded functions */ NTSTATUS (WINAPI *func_RtlDosPathNameToNtPathName_U_WithStatus) (IN PCWSTR DosName, OUT PUNICODE_STRING NtName, OUT PCWSTR *PartName, OUT PRTL_RELATIVE_NAME_U RelativeName); NTSTATUS (WINAPI *func_RtlCreateSystemVolumeInformationFolder) (PCUNICODE_STRING VolumeRootPath); static bool acquired_privileges = false; static HMODULE ntdll_handle = NULL; static int init_ntdll(void) { ntdll_handle = LoadLibrary(L"ntdll.dll"); if (!ntdll_handle) { ERROR("Unable to load ntdll.dll"); return WIMLIB_ERR_UNSUPPORTED; } func_RtlDosPathNameToNtPathName_U_WithStatus = (void *)GetProcAddress(ntdll_handle, "RtlDosPathNameToNtPathName_U_WithStatus"); func_RtlCreateSystemVolumeInformationFolder = (void *)GetProcAddress(ntdll_handle, "RtlCreateSystemVolumeInformationFolder"); return 0; } /* One-time initialization for Windows capture/apply code. */ int win32_global_init(int init_flags) { int ret; /* Try to acquire useful privileges. */ if (!(init_flags & WIMLIB_INIT_FLAG_DONT_ACQUIRE_PRIVILEGES)) { ret = WIMLIB_ERR_INSUFFICIENT_PRIVILEGES; if (!win32_modify_capture_privileges(true)) if (init_flags & WIMLIB_INIT_FLAG_STRICT_CAPTURE_PRIVILEGES) goto out_drop_privs; if (!win32_modify_apply_privileges(true)) if (init_flags & WIMLIB_INIT_FLAG_STRICT_APPLY_PRIVILEGES) goto out_drop_privs; acquired_privileges = true; } ret = init_ntdll(); if (ret) goto out_drop_privs; return 0; out_drop_privs: win32_release_capture_and_apply_privileges(); return ret; } void win32_global_cleanup(void) { vss_global_cleanup(); if (acquired_privileges) win32_release_capture_and_apply_privileges(); FreeLibrary(ntdll_handle); ntdll_handle = NULL; } /* * Translates a Win32-namespace path into an NT-namespace path. * * On success, returns 0. The NT-namespace path will be stored in the * UNICODE_STRING structure pointed to by nt_path. nt_path->Buffer will be set * to a new buffer that must later be freed with HeapFree(). (Really * RtlHeapFree(), but HeapFree() seems to be the same thing.) * * On failure, returns WIMLIB_ERR_NOMEM or WIMLIB_ERR_INVALID_PARAM. */ int win32_path_to_nt_path(const wchar_t *win32_path, UNICODE_STRING *nt_path) { NTSTATUS status; if (func_RtlDosPathNameToNtPathName_U_WithStatus) { status = (*func_RtlDosPathNameToNtPathName_U_WithStatus)(win32_path, nt_path, NULL, NULL); } else { if (RtlDosPathNameToNtPathName_U(win32_path, nt_path, NULL, NULL)) status = STATUS_SUCCESS; else status = STATUS_NO_MEMORY; } if (likely(NT_SUCCESS(status))) return 0; if (status == STATUS_NO_MEMORY) return WIMLIB_ERR_NOMEM; winnt_error(status, L"\"%ls\": invalid path name", win32_path); return WIMLIB_ERR_INVALID_PARAM; } int win32_get_drive_path(const wchar_t *file_path, wchar_t drive_path[7]) { tchar *file_abspath; file_abspath = realpath(file_path, NULL); if (!file_abspath) return WIMLIB_ERR_NOMEM; if (file_abspath[0] == L'\0' || file_abspath[1] != L':') { ERROR("\"%ls\": Path format not recognized", file_abspath); FREE(file_abspath); return WIMLIB_ERR_UNSUPPORTED; } wsprintf(drive_path, L"\\\\.\\%lc:", file_abspath[0]); FREE(file_abspath); return 0; } /* Try to attach an instance of the Windows Overlay Filesystem filter driver to * the specified drive (such as C:) */ bool win32_try_to_attach_wof(const wchar_t *drive) { HMODULE fltlib; bool retval = false; /* Use FilterAttach() from Fltlib.dll. */ fltlib = LoadLibrary(L"Fltlib.dll"); if (!fltlib) { WARNING("Failed to load Fltlib.dll"); return retval; } HRESULT (WINAPI *func_FilterAttach)(LPCWSTR lpFilterName, LPCWSTR lpVolumeName, LPCWSTR lpInstanceName, DWORD dwCreatedInstanceNameLength, LPWSTR lpCreatedInstanceName); func_FilterAttach = (void *)GetProcAddress(fltlib, "FilterAttach"); if (func_FilterAttach) { HRESULT res; res = (*func_FilterAttach)(L"wof", drive, NULL, 0, NULL); if (res != S_OK) res = (*func_FilterAttach)(L"wofadk", drive, NULL, 0, NULL); if (res == S_OK) retval = true; } else { WARNING("FilterAttach() does not exist in Fltlib.dll"); } FreeLibrary(fltlib); return retval; } static void windows_msg(u32 code, const wchar_t *format, va_list va, bool is_ntstatus, bool is_error) { wchar_t _buf[STACK_MAX / 8]; wchar_t *buf = _buf; size_t buflen = ARRAY_LEN(_buf); size_t ret; size_t n; va_list va2; retry: va_copy(va2, va); n = vsnwprintf(buf, buflen, format, va2); va_end(va2); if (n >= buflen) goto realloc; n += snwprintf(&buf[n], buflen - n, (is_ntstatus ? L" (status=%08"PRIx32"): " : L" (err=%"PRIu32"): "), code); if (n >= buflen) goto realloc; ret = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | (is_ntstatus ? FORMAT_MESSAGE_FROM_HMODULE : 0), (is_ntstatus ? ntdll_handle : NULL), code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), &buf[n], buflen - n, NULL); n += ret; if (n >= buflen || (ret == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)) goto realloc; if (buf[n - 1] == L'\n') buf[--n] = L'\0'; if (buf[n - 1] == L'\r') buf[--n] = L'\0'; if (buf[n - 1] == L'.') buf[--n] = L'\0'; if (is_error) ERROR("%ls", buf); else WARNING("%ls", buf); if (buf != _buf) FREE(buf); return; realloc: if (buf != _buf) FREE(buf); buflen *= 2; buf = MALLOC(buflen * sizeof(buf[0])); if (buf) goto retry; ERROR("Ran out of memory while building error message!!!"); } void win32_warning(DWORD err, const wchar_t *format, ...) { va_list va; va_start(va, format); windows_msg(err, format, va, false, false); va_end(va); } void win32_error(DWORD err, const wchar_t *format, ...) { va_list va; va_start(va, format); windows_msg(err, format, va, false, true); va_end(va); } void winnt_warning(NTSTATUS status, const wchar_t *format, ...) { va_list va; va_start(va, format); windows_msg(status, format, va, true, false); va_end(va); } void winnt_error(NTSTATUS status, const wchar_t *format, ...) { va_list va; va_start(va, format); windows_msg(status, format, va, true, true); va_end(va); } /* * Synchronously execute a filesystem control method. This is a wrapper around * NtFsControlFile() that handles STATUS_PENDING. Note that SYNCHRONIZE * permission is, in general, required on the handle. */ NTSTATUS winnt_fsctl(HANDLE h, u32 code, const void *in, u32 in_size, void *out, u32 out_size_avail, u32 *actual_out_size_ret) { IO_STATUS_BLOCK iosb; NTSTATUS status; status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, code, (void *)in, in_size, out, out_size_avail); if (status == STATUS_PENDING) { /* Beware: this case is often encountered with remote * filesystems, but rarely with local filesystems. */ status = NtWaitForSingleObject(h, FALSE, NULL); if (NT_SUCCESS(status)) { status = iosb.Status; } else { /* We shouldn't be issuing ioctls on a handle to which * we don't have SYNCHRONIZE access. Otherwise we have * no way to wait for them to complete. */ wimlib_assert(status != STATUS_ACCESS_DENIED); } } if (NT_SUCCESS(status) && actual_out_size_ret != NULL) *actual_out_size_ret = (u32)iosb.Information; return status; } #endif /* __WIN32__ */ wimlib-1.13.1/src/scan.c0000644000175000017500000003273113324671461011675 00000000000000/* * scan.c - Helper routines for directory tree scans */ /* * Copyright (C) 2013-2017 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include "wimlib/blob_table.h" #include "wimlib/dentry.h" #include "wimlib/error.h" #include "wimlib/paths.h" #include "wimlib/pattern.h" #include "wimlib/progress.h" #include "wimlib/scan.h" #include "wimlib/textfile.h" /* * Tally a file (or directory) that has been scanned for a capture operation, * and possibly call the progress function provided by the library user. * * @params * Current path, flags, optional progress function, and progress data for * the scan operation. * @status * Status of the scanned file. * @inode * If @status is WIMLIB_SCAN_DENTRY_OK, this is a pointer to the WIM inode * that has been created for the scanned file. The first time the file is * seen, inode->i_nlink will be 1. On subsequent visits of the same inode * via additional hard links, inode->i_nlink will be greater than 1. */ int do_scan_progress(struct scan_params *params, int status, const struct wim_inode *inode) { int ret; tchar *cookie; switch (status) { case WIMLIB_SCAN_DENTRY_OK: if (!(params->add_flags & WIMLIB_ADD_FLAG_VERBOSE)) return 0; break; case WIMLIB_SCAN_DENTRY_UNSUPPORTED: case WIMLIB_SCAN_DENTRY_EXCLUDED: case WIMLIB_SCAN_DENTRY_FIXED_SYMLINK: case WIMLIB_SCAN_DENTRY_NOT_FIXED_SYMLINK: if (!(params->add_flags & WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE)) return 0; break; } params->progress.scan.cur_path = params->cur_path; params->progress.scan.status = status; if (status == WIMLIB_SCAN_DENTRY_OK) { /* The first time the inode is seen, tally all its streams. */ if (inode->i_nlink == 1) { for (unsigned i = 0; i < inode->i_num_streams; i++) { const struct blob_descriptor *blob = stream_blob_resolved(&inode->i_streams[i]); if (blob) params->progress.scan.num_bytes_scanned += blob->size; } } /* Tally the file itself, counting every hard link. It's * debatable whether every link should be counted, but counting * every link makes the statistics consistent with the ones * placed in the FILECOUNT and DIRCOUNT elements of the WIM * file's XML document. It also avoids possible user confusion * if the number of files reported were to be lower than that * displayed by some other software such as file browsers. */ if (inode_is_directory(inode)) params->progress.scan.num_dirs_scanned++; else params->progress.scan.num_nondirs_scanned++; } /* Call the user-provided progress function. */ cookie = progress_get_win32_path(params->progress.scan.cur_path); ret = call_progress(params->progfunc, WIMLIB_PROGRESS_MSG_SCAN_DENTRY, ¶ms->progress, params->progctx); progress_put_win32_path(cookie); return ret; } /* * Given a null-terminated pathname pattern @pat that has been read from line * @line_no of the file @path, validate and canonicalize the pattern. * * On success, returns 0. * On failure, returns WIMLIB_ERR_INVALID_CAPTURE_CONFIG. * In either case, @pat may have been modified in-place (and possibly * shortened). */ int mangle_pat(tchar *pat, const tchar *path, unsigned long line_no) { if (!is_any_path_separator(pat[0]) && pat[0] != T('\0') && pat[1] == T(':')) { /* Pattern begins with drive letter. */ if (!is_any_path_separator(pat[2])) { /* Something like c:file, which is actually a path * relative to the current working directory on the c: * drive. We require paths with drive letters to be * absolute. */ ERROR("%"TS":%lu: Invalid pattern \"%"TS"\":\n" " Patterns including drive letters must be absolute!\n" " Maybe try \"%"TC":%"TC"%"TS"\"?\n", path, line_no, pat, pat[0], OS_PREFERRED_PATH_SEPARATOR, &pat[2]); return WIMLIB_ERR_INVALID_CAPTURE_CONFIG; } WARNING("%"TS":%lu: Pattern \"%"TS"\" starts with a drive " "letter, which is being removed.", path, line_no, pat); /* Strip the drive letter. */ tmemmove(pat, pat + 2, tstrlen(pat + 2) + 1); } /* Collapse consecutive path separators, and translate both / and \ into * / (UNIX) or \ (Windows). * * Note: we expect that this function produces patterns that can be used * for both filesystem paths and WIM paths, so the desired path * separators must be the same. */ STATIC_ASSERT(OS_PREFERRED_PATH_SEPARATOR == WIM_PATH_SEPARATOR); do_canonicalize_path(pat, pat); /* Relative patterns can only match file names, so they must be * single-component only. */ if (pat[0] != OS_PREFERRED_PATH_SEPARATOR && tstrchr(pat, OS_PREFERRED_PATH_SEPARATOR)) { ERROR("%"TS":%lu: Invalid pattern \"%"TS"\":\n" " Relative patterns can only include one path component!\n" " Maybe try \"%"TC"%"TS"\"?", path, line_no, pat, OS_PREFERRED_PATH_SEPARATOR, pat); return WIMLIB_ERR_INVALID_CAPTURE_CONFIG; } return 0; } /* * Read, parse, and validate a capture configuration file from either an on-disk * file or an in-memory buffer. * * To read from a file, specify @config_file, and use NULL for @buf. * To read from a buffer, specify @buf and @bufsize. * * @config must be initialized to all 0's. * * On success, 0 will be returned, and the resulting capture configuration will * be stored in @config. * * On failure, a positive error code will be returned, and the contents of * @config will be invalidated. */ int read_capture_config(const tchar *config_file, const void *buf, size_t bufsize, struct capture_config *config) { int ret; /* [PrepopulateList] is used for apply, not capture. But since we do * understand it, recognize it, thereby avoiding the unrecognized * section warning, but discard the resulting strings. * * We currently ignore [CompressionExclusionList] and * [CompressionFolderList]. This is a known issue that doesn't seem to * have any real consequences, so don't issue warnings about not * recognizing those sections. */ STRING_LIST(prepopulate_pats); STRING_LIST(compression_exclusion_pats); STRING_LIST(compression_folder_pats); struct text_file_section sections[] = { {T("ExclusionList"), &config->exclusion_pats}, {T("ExclusionException"), &config->exclusion_exception_pats}, {T("PrepopulateList"), &prepopulate_pats}, {T("CompressionExclusionList"), &compression_exclusion_pats}, {T("CompressionFolderList"), &compression_folder_pats}, }; void *mem; ret = load_text_file(config_file, buf, bufsize, &mem, sections, ARRAY_LEN(sections), LOAD_TEXT_FILE_REMOVE_QUOTES, mangle_pat); if (ret) { ERROR("Failed to load capture configuration file \"%"TS"\"", config_file); switch (ret) { case WIMLIB_ERR_INVALID_UTF8_STRING: case WIMLIB_ERR_INVALID_UTF16_STRING: ERROR("Note: the capture configuration file must be " "valid UTF-8 or UTF-16LE"); ret = WIMLIB_ERR_INVALID_CAPTURE_CONFIG; break; case WIMLIB_ERR_OPEN: case WIMLIB_ERR_STAT: case WIMLIB_ERR_NOMEM: case WIMLIB_ERR_READ: ret = WIMLIB_ERR_UNABLE_TO_READ_CAPTURE_CONFIG; break; } return ret; } FREE(prepopulate_pats.strings); FREE(compression_exclusion_pats.strings); FREE(compression_folder_pats.strings); config->buf = mem; return 0; } void destroy_capture_config(struct capture_config *config) { FREE(config->exclusion_pats.strings); FREE(config->exclusion_exception_pats.strings); FREE(config->buf); } /* * Determine whether @path matches any of the patterns in @list. * Path separators in @path must be WIM_PATH_SEPARATOR. */ bool match_pattern_list(const tchar *path, const struct string_list *list, int match_flags) { for (size_t i = 0; i < list->num_strings; i++) if (match_path(path, list->strings[i], match_flags)) return true; return false; } /* * Determine if a file should be excluded from capture. * * This function tests exclusions from both possible sources of exclusions: * * (1) The capture configuration file * (2) The user-provided progress function * * params->root_path_nchars must have been set beforehand. Example for UNIX: if * the capture root directory is "foobar/subdir", then all paths will be * provided starting with "foobar/subdir", so params->root_path_nchars must have * been set to strlen("foobar/subdir") so that the appropriate path suffix can * be matched against the patterns in the exclusion list. * * Returns: * < 0 if excluded * = 0 if not excluded and no error * > 0 (wimlib error code) if error */ int try_exclude(const struct scan_params *params) { int ret; if (params->config) { const tchar *path = params->cur_path + params->root_path_nchars; if (match_pattern_list(path, ¶ms->config->exclusion_pats, MATCH_RECURSIVELY) && !match_pattern_list(path, ¶ms->config->exclusion_exception_pats, MATCH_RECURSIVELY | MATCH_ANCESTORS)) return -1; } if (unlikely(params->add_flags & WIMLIB_ADD_FLAG_TEST_FILE_EXCLUSION)) { union wimlib_progress_info info; tchar *cookie; info.test_file_exclusion.path = params->cur_path; info.test_file_exclusion.will_exclude = false; cookie = progress_get_win32_path(info.test_file_exclusion.path); ret = call_progress(params->progfunc, WIMLIB_PROGRESS_MSG_TEST_FILE_EXCLUSION, &info, params->progctx); progress_put_win32_path(cookie); if (ret) return ret; if (info.test_file_exclusion.will_exclude) return -1; } return 0; } /* * Determine whether a directory entry of the specified name should be ignored. * This is a lower level function which runs prior to try_exclude(). It handles * the standard '.' and '..' entries, which show up in directory listings but * should not be archived. It also checks for odd filenames that usually should * not exist but could cause problems if archiving them were to be attempted. */ bool should_ignore_filename(const tchar *name, const int name_nchars) { if (name_nchars <= 0) { WARNING("Ignoring empty filename"); return true; } if (name[0] == T('.') && (name_nchars == 1 || (name_nchars == 2 && name[1] == T('.')))) return true; for (int i = 0; i < name_nchars; i++) { if (name[i] == T('\0')) { WARNING("Ignoring filename containing embedded null character"); return true; } if (name[i] == OS_PREFERRED_PATH_SEPARATOR) { WARNING("Ignoring filename containing embedded path separator"); return true; } } return false; } /* Attach a newly scanned directory tree to its parent directory, with duplicate * handling. */ void attach_scanned_tree(struct wim_dentry *parent, struct wim_dentry *child, struct blob_table *blob_table) { struct wim_dentry *duplicate; if (child && (duplicate = dentry_add_child(parent, child))) { WARNING("Duplicate file path: \"%"TS"\". Only capturing " "the first version.", dentry_full_path(duplicate)); free_dentry_tree(child, blob_table); } } /* Set the path at which the directory tree scan is beginning. */ int pathbuf_init(struct scan_params *params, const tchar *root_path) { size_t nchars = tstrlen(root_path); size_t alloc_nchars = nchars + 1 + 1024; params->cur_path = MALLOC(alloc_nchars * sizeof(tchar)); if (!params->cur_path) return WIMLIB_ERR_NOMEM; tmemcpy(params->cur_path, root_path, nchars + 1); params->cur_path_nchars = nchars; params->cur_path_alloc_nchars = alloc_nchars; params->root_path_nchars = nchars; return 0; } /* * Append a filename to the current path. * * If successful, returns a pointer to the filename component and sets * *orig_path_nchars_ret to the old path length, which can be restored later * using pathbuf_truncate(). Otherwise returns NULL (out of memory). */ const tchar * pathbuf_append_name(struct scan_params *params, const tchar *name, size_t name_nchars, size_t *orig_path_nchars_ret) { size_t path_nchars = params->cur_path_nchars; size_t required_nchars = path_nchars + 1 + name_nchars + 1; tchar *buf = params->cur_path; if (unlikely(required_nchars > params->cur_path_alloc_nchars)) { required_nchars += 1024; buf = REALLOC(buf, required_nchars * sizeof(tchar)); if (!buf) return NULL; params->cur_path = buf; params->cur_path_alloc_nchars = required_nchars; } *orig_path_nchars_ret = path_nchars; /* * Add the slash, but not if it will be a duplicate (which can happen if * the path to the capture root directory ends in a slash), because * on Windows duplicate slashes sometimes don't work as expected. */ if (path_nchars && buf[path_nchars - 1] != OS_PREFERRED_PATH_SEPARATOR) buf[path_nchars++] = OS_PREFERRED_PATH_SEPARATOR; tmemcpy(&buf[path_nchars], name, name_nchars); path_nchars += name_nchars; buf[path_nchars] = T('\0'); params->cur_path_nchars = path_nchars; return &buf[path_nchars - name_nchars]; } /* Truncate the current path to the specified number of characters. */ void pathbuf_truncate(struct scan_params *params, size_t nchars) { wimlib_assert(nchars <= params->cur_path_nchars); params->cur_path[nchars] = T('\0'); params->cur_path_nchars = nchars; } wimlib-1.13.1/src/template.c0000644000175000017500000001246413160354225012557 00000000000000/* * template.c * * API to reference a template image to optimize later writing of a WIM file. */ /* * Copyright (C) 2013, 2015 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib.h" #include "wimlib/blob_table.h" #include "wimlib/assert.h" #include "wimlib/dentry.h" #include "wimlib/error.h" #include "wimlib/metadata.h" #include "wimlib/util.h" static u64 stream_size(const struct wim_inode_stream *strm, const struct blob_table *blob_table) { const struct blob_descriptor *blob; blob = stream_blob(strm, blob_table); if (!blob) return 0; return blob->size; } /* Returns %true iff the metadata of @inode and @template_inode are reasonably * consistent with them being the same, unmodified file. */ static bool inode_metadata_consistent(const struct wim_inode *inode, const struct wim_inode *template_inode, const struct blob_table *blob_table, const struct blob_table *template_blob_table) { /* Must have exact same creation time and last write time. */ if (inode->i_creation_time != template_inode->i_creation_time || inode->i_last_write_time != template_inode->i_last_write_time) return false; /* Last access time may have stayed the same or increased, but certainly * shouldn't have decreased. */ if (inode->i_last_access_time < template_inode->i_last_access_time) return false; /* All stream sizes must match. */ for (unsigned i = 0; i < inode->i_num_streams; i++) { const struct wim_inode_stream *strm, *template_strm; strm = &inode->i_streams[i]; template_strm = inode_get_stream(template_inode, strm->stream_type, strm->stream_name); if (!template_strm) return false; if (stream_size(strm, blob_table) != stream_size(template_strm, template_blob_table)) return false; } return true; } /** * Given an inode @inode that has been determined to be "the same" as another * inode @template_inode in either the same WIM or another WIM, copy stream * checksums from @template_inode to @inode. */ static void inode_copy_checksums(struct wim_inode *inode, struct wim_inode *template_inode, struct blob_table *blob_table, struct blob_table *template_blob_table) { for (unsigned i = 0; i < inode->i_num_streams; i++) { const struct wim_inode_stream *strm, *template_strm; struct blob_descriptor *blob, *template_blob, **back_ptr; strm = &inode->i_streams[i]; template_strm = inode_get_stream(template_inode, strm->stream_type, strm->stream_name); blob = stream_blob(strm, blob_table); template_blob = stream_blob(template_strm, template_blob_table); /* To copy hashes: both blobs must exist, the blob for @inode * must be unhashed, and the blob for @template_inode must be * hashed. */ if (!blob || !template_blob || !blob->unhashed || template_blob->unhashed) continue; back_ptr = retrieve_pointer_to_unhashed_blob(blob); copy_hash(blob->hash, template_blob->hash); if (after_blob_hashed(blob, back_ptr, blob_table) != blob) free_blob_descriptor(blob); } } static int reference_template_file(struct wim_inode *inode, WIMStruct *wim, WIMStruct *template_wim) { struct wim_dentry *dentry = inode_any_dentry(inode); struct wim_dentry *template_dentry; int ret; ret = calculate_dentry_full_path(dentry); if (ret) return ret; template_dentry = get_dentry(template_wim, dentry->d_full_path, WIMLIB_CASE_SENSITIVE); if (template_dentry != NULL && inode_metadata_consistent(inode, template_dentry->d_inode, wim->blob_table, template_wim->blob_table)) { inode_copy_checksums(inode, template_dentry->d_inode, wim->blob_table, template_wim->blob_table); } FREE(dentry->d_full_path); dentry->d_full_path = NULL; return 0; } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_reference_template_image(WIMStruct *wim, int new_image, WIMStruct *template_wim, int template_image, int flags) { int ret; struct wim_image_metadata *new_imd; struct wim_inode *inode; if (flags != 0) return WIMLIB_ERR_INVALID_PARAM; if (wim == NULL || template_wim == NULL) return WIMLIB_ERR_INVALID_PARAM; if (wim == template_wim && new_image == template_image) return WIMLIB_ERR_INVALID_PARAM; if (new_image < 1 || new_image > wim->hdr.image_count) return WIMLIB_ERR_INVALID_IMAGE; if (!wim_has_metadata(wim)) return WIMLIB_ERR_METADATA_NOT_FOUND; new_imd = wim->image_metadata[new_image - 1]; if (!is_image_dirty(new_imd)) return WIMLIB_ERR_INVALID_PARAM; ret = select_wim_image(template_wim, template_image); if (ret) return ret; image_for_each_inode(inode, new_imd) { ret = reference_template_file(inode, wim, template_wim); if (ret) return ret; } return 0; } wimlib-1.13.1/src/encoding.c0000644000175000017500000003554013160354224012531 00000000000000/* * encoding.c - UTF-8 and UTF-16LE codecs and utility functions * * Copyright (C) 2012-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include "wimlib/encoding.h" #include "wimlib/endianness.h" #include "wimlib/error.h" #include "wimlib/unaligned.h" #include "wimlib/util.h" /* * Allow unpaired surrogates, such as might exist in Windows-style filenames --- * which are normally valid UTF-16LE, but are actually treated as opaque * sequences of 16-bit WCHARs by Windows. When decoding "UTF-16LE", unpaired * surrogates will be decoded as their surrogate codepoints; and when encoding * to and from "UTF-8", the encoding will actually be WTF-8 ("Wobbly * Transformation Format - 8-bit"), a superset of UTF-8 which permits the * surrogate codepoints. * * In combination with also allowing the "non-character" codepoints U+FFFE and * U+FFFF, the result is that every Windows-style filename can be translated to * a UNIX-style filename. * * Unfortunately, the converse is not true: not every UNIX filename can be * translated to a Windows filename. Only UNIX filenames that are valid "WTF-8" * can be translated. I considered ways to define a bijective mapping, but * there did not seem to be a straightforward way. The "UTF-8b" scheme, for * example, would map each invalid byte 'b' to a surrogate "escape code" 'U+DC00 * + b'. The problem with this was that surrogate escape codes can be combined * to create a valid UTF-8 sequence, thus breaking the bijection by mapping * multiple Windows filenames to a single UNIX filename. */ #define ALLOW_UNPAIRED_SURROGATES 1 #define INVALID_CODEPOINT 0xFFFFFFFF #define VALIDATE(expr) if (validate && unlikely(!(expr))) goto invalid #define IS_SURROGATE(c) ((c) >= 0xD800 && (c) < 0xE000) #define IS_HIGH_SURROGATE(c) ((c) >= 0xD800 && (c) < 0xDC00) #define IS_LOW_SURROGATE(c) ((c) >= 0xDC00 && (c) < 0xE000) #define IS_UTF8_TAIL(c) (((c) & 0xC0) == 0x80) /* * Decode the next Unicode codepoint from the string at @in, which has * @remaining >= 1 bytes remaining. Return the number of bytes consumed and * write the decoded codepoint to *c_ret. * * If the input might not be a valid string in the source encoding, then * @validate must be specified as %true, and then on invalid input the function * consumes at least one byte and sets *c_ret to INVALID_CODEPOINT. If the * input is guaranteed to be valid, then @validate may be specified as %false. */ typedef unsigned (*decode_codepoint_fn)(const u8 *in, size_t remaining, bool validate, u32 *c_ret); /* Encode the Unicode codepoint @c and return the number of bytes used. */ typedef unsigned (*encode_codepoint_fn)(u32 c, u8 *out); static forceinline unsigned utf8_decode_codepoint(const u8 *in, size_t remaining, bool validate, u32 *c_ret) { if (likely(in[0] < 0x80)) { /* U+0...U+7F */ *c_ret = in[0]; return 1; } if (in[0] < 0xE0) { /* U+80...U+7FF */ VALIDATE(in[0] >= 0xC2 && remaining >= 2 && IS_UTF8_TAIL(in[1])); *c_ret = ((u32)(in[0] & 0x1F) << 6) | ((u32)(in[1] & 0x3F) << 0); return 2; } if (in[0] < 0xF0) { /* U+800...U+FFFF, possibly excluding surrogates */ VALIDATE(remaining >= 3 && IS_UTF8_TAIL(in[1]) && IS_UTF8_TAIL(in[2])); *c_ret = ((u32)(in[0] & 0x0F) << 12) | ((u32)(in[1] & 0x3F) << 6) | ((u32)(in[2] & 0x3F) << 0); VALIDATE(*c_ret >= 0x800); #if !ALLOW_UNPAIRED_SURROGATES VALIDATE(!IS_SURROGATE(*c_ret)); #endif return 3; } /* U+10000...U+10FFFF */ VALIDATE(in[0] < 0xF8 && remaining >= 4 && IS_UTF8_TAIL(in[1]) && IS_UTF8_TAIL(in[2]) && IS_UTF8_TAIL(in[3])); *c_ret = ((u32)(in[0] & 0x07) << 18) | ((u32)(in[1] & 0x3F) << 12) | ((u32)(in[2] & 0x3F) << 6) | ((u32)(in[3] & 0x3F) << 0); VALIDATE(*c_ret >= 0x10000 && *c_ret <= 0x10FFFF); return 4; invalid: *c_ret = INVALID_CODEPOINT; return 1; } static forceinline unsigned utf8_encode_codepoint(u32 c, u8 *out) { if (likely(c < 0x80)) { out[0] = c; return 1; } if (c < 0x800) { out[0] = 0xC0 | (c >> 6); out[1] = 0x80 | (c & 0x3F); return 2; } if (c < 0x10000) { out[0] = 0xE0 | (c >> 12); out[1] = 0x80 | ((c >> 6) & 0x3F); out[2] = 0x80 | (c & 0x3F); return 3; } out[0] = 0xF0 | (c >> 18); out[1] = 0x80 | ((c >> 12) & 0x3F); out[2] = 0x80 | ((c >> 6) & 0x3F); out[3] = 0x80 | (c & 0x3F); return 4; } static forceinline unsigned utf16le_decode_codepoint(const u8 *in, size_t remaining, bool validate, u32 *c_ret) { u32 h, l; VALIDATE(remaining >= 2); h = get_unaligned_le16(in); if (unlikely(IS_SURROGATE(h))) { /* Surrogate pairs are U+10000...U+10FFFF. * Unpaired surrogates are U+D800...U+DFFF. */ #if ALLOW_UNPAIRED_SURROGATES if (unlikely(!IS_HIGH_SURROGATE(h) || remaining < 4)) goto unpaired; l = get_unaligned_le16(in + 2); if (unlikely(!IS_LOW_SURROGATE(l))) goto unpaired; #else VALIDATE(IS_HIGH_SURROGATE(h) && remaining >= 4); l = get_unaligned_le16(in + 2); VALIDATE(IS_LOW_SURROGATE(l)); #endif *c_ret = 0x10000 + ((h - 0xD800) << 10) + (l - 0xDC00); return 4; } #if ALLOW_UNPAIRED_SURROGATES unpaired: #endif *c_ret = h; return 2; invalid: *c_ret = INVALID_CODEPOINT; return min(remaining, 2); } static forceinline unsigned utf16le_encode_codepoint(u32 c, u8 *out) { if (likely(c < 0x10000)) { put_unaligned_le16(c, out); return 2; } c -= 0x10000; put_unaligned_le16(0xD800 + (c >> 10), out); put_unaligned_le16(0xDC00 + (c & 0x3FF), out + 2); return 4; } /* * Convert the string @in of size @in_nbytes from the encoding given by the * @decode_codepoint function to the encoding given by the @encode_codepoint * function. @in does not need to be null-terminated, but a null terminator * will be added to the output string. * * On success, write the allocated output string to @out_ret (must not be NULL) * and its size excluding the null terminator to @out_nbytes_ret (may be NULL). * * If the input string is malformed, return @ilseq_err with errno set to EILSEQ. * If out of memory, return WIMLIB_ERR_NOMEM with errno set to ENOMEM. */ static forceinline int convert_string(const u8 * const in, const size_t in_nbytes, u8 **out_ret, size_t *out_nbytes_ret, int ilseq_err, decode_codepoint_fn decode_codepoint, encode_codepoint_fn encode_codepoint) { const u8 * const in_end = in + in_nbytes; const u8 *p_in; u8 *p_out; size_t out_nbytes = 0; u8 *out; u8 tmp[8]; /* assuming no codepoint requires > 8 bytes to encode */ u32 c; /* Validate the input string and compute the output size. */ for (p_in = in; p_in != in_end; ) { p_in += (*decode_codepoint)(p_in, in_end - p_in, true, &c); if (unlikely(c == INVALID_CODEPOINT)) { errno = EILSEQ; return ilseq_err; } out_nbytes += (*encode_codepoint)(c, tmp); } /* Allocate the output string, including space for a null terminator. */ out = MALLOC(out_nbytes + (*encode_codepoint)(0, tmp)); if (unlikely(!out)) return WIMLIB_ERR_NOMEM; /* Do the conversion. */ for (p_in = in, p_out = out; p_in != in_end; ) { p_in += (*decode_codepoint)(p_in, in_end - p_in, false, &c); p_out += (*encode_codepoint)(c, p_out); } /* Add a null terminator. */ (*encode_codepoint)(0, p_out); /* Return the output string and its size (by reference). */ *out_ret = out; if (out_nbytes_ret) *out_nbytes_ret = out_nbytes; return 0; } int utf8_to_utf16le(const char *in, size_t in_nbytes, utf16lechar **out_ret, size_t *out_nbytes_ret) { return convert_string((const u8 *)in, in_nbytes, (u8 **)out_ret, out_nbytes_ret, WIMLIB_ERR_INVALID_UTF8_STRING, utf8_decode_codepoint, utf16le_encode_codepoint); } int utf16le_to_utf8(const utf16lechar *in, size_t in_nbytes, char **out_ret, size_t *out_nbytes_ret) { return convert_string((const u8 *)in, in_nbytes, (u8 **)out_ret, out_nbytes_ret, WIMLIB_ERR_INVALID_UTF16_STRING, utf16le_decode_codepoint, utf8_encode_codepoint); } /* * A table that maps from UCS-2 characters to their upper case equivalents. * Index and array values are both CPU endian. * Note: this is only an *approximation* of real UTF-16 case folding. */ u16 upcase[65536]; void init_upcase(void) { /* This is the table used in NTFS volumes formatted by Windows 10. * It was compressed by tools/compress_upcase_table.c. */ static const u16 upcase_compressed[] = { 0x0000, 0x0000, 0x0060, 0x0000, 0x0000, 0xffe0, 0x0019, 0x0061, 0x0061, 0x0000, 0x001b, 0x005d, 0x0008, 0x0060, 0x0000, 0x0079, 0x0000, 0x0000, 0x0000, 0xffff, 0x002f, 0x0100, 0x0002, 0x0000, 0x0007, 0x012b, 0x0011, 0x0121, 0x002f, 0x0103, 0x0006, 0x0101, 0x0000, 0x00c3, 0x0006, 0x0131, 0x0007, 0x012e, 0x0004, 0x0000, 0x0003, 0x012f, 0x0000, 0x0061, 0x0004, 0x0130, 0x0000, 0x00a3, 0x0003, 0x0000, 0x0000, 0x0082, 0x000b, 0x0131, 0x0006, 0x0189, 0x0008, 0x012f, 0x0007, 0x012e, 0x0000, 0x0038, 0x0006, 0x0000, 0x0000, 0xfffe, 0x0007, 0x01c4, 0x000f, 0x0101, 0x0000, 0xffb1, 0x0015, 0x011e, 0x0004, 0x01cc, 0x002a, 0x0149, 0x0014, 0x0149, 0x0007, 0x0000, 0x0009, 0x018c, 0x000b, 0x0138, 0x0000, 0x2a1f, 0x0000, 0x2a1c, 0x0000, 0x0000, 0x0000, 0xff2e, 0x0000, 0xff32, 0x0000, 0x0000, 0x0000, 0xff33, 0x0000, 0xff33, 0x0000, 0x0000, 0x0000, 0xff36, 0x0000, 0x0000, 0x0000, 0xff35, 0x0004, 0x0000, 0x0002, 0x0257, 0x0000, 0x0000, 0x0000, 0xff31, 0x0004, 0x0000, 0x0000, 0xff2f, 0x0000, 0xff2d, 0x0000, 0x0000, 0x0000, 0x29f7, 0x0003, 0x0000, 0x0002, 0x0269, 0x0000, 0x29fd, 0x0000, 0xff2b, 0x0002, 0x0000, 0x0000, 0xff2a, 0x0007, 0x0000, 0x0000, 0x29e7, 0x0002, 0x0000, 0x0000, 0xff26, 0x0005, 0x027e, 0x0003, 0x027e, 0x0000, 0xffbb, 0x0000, 0xff27, 0x0000, 0xff27, 0x0000, 0xffb9, 0x0005, 0x0000, 0x0000, 0xff25, 0x0065, 0x007b, 0x0079, 0x0293, 0x0008, 0x012d, 0x0003, 0x019c, 0x0002, 0x037b, 0x002e, 0x0000, 0x0000, 0xffda, 0x0000, 0xffdb, 0x0002, 0x03ad, 0x0012, 0x0060, 0x000a, 0x0060, 0x0000, 0xffc0, 0x0000, 0xffc1, 0x0000, 0xffc1, 0x0008, 0x0000, 0x0000, 0xfff8, 0x001a, 0x0118, 0x0000, 0x0007, 0x0008, 0x018d, 0x0009, 0x0233, 0x0046, 0x0035, 0x0006, 0x0061, 0x0000, 0xffb0, 0x000f, 0x0450, 0x0025, 0x010e, 0x000a, 0x036b, 0x0032, 0x048b, 0x000e, 0x0100, 0x0000, 0xfff1, 0x0037, 0x048a, 0x0026, 0x0465, 0x0034, 0x0000, 0x0000, 0xffd0, 0x0025, 0x0561, 0x00de, 0x0293, 0x1714, 0x0587, 0x0000, 0x8a04, 0x0003, 0x0000, 0x0000, 0x0ee6, 0x0087, 0x02ee, 0x0092, 0x1e01, 0x0069, 0x1df7, 0x0000, 0x0008, 0x0007, 0x1f00, 0x0008, 0x0000, 0x000e, 0x1f02, 0x0008, 0x1f0e, 0x0010, 0x1f06, 0x001a, 0x1f06, 0x0002, 0x1f0f, 0x0007, 0x1f50, 0x0017, 0x1f19, 0x0000, 0x004a, 0x0000, 0x004a, 0x0000, 0x0056, 0x0003, 0x1f72, 0x0000, 0x0064, 0x0000, 0x0064, 0x0000, 0x0080, 0x0000, 0x0080, 0x0000, 0x0070, 0x0000, 0x0070, 0x0000, 0x007e, 0x0000, 0x007e, 0x0028, 0x1f1e, 0x000c, 0x1f06, 0x0000, 0x0000, 0x0000, 0x0009, 0x000f, 0x0000, 0x000d, 0x1fb3, 0x000d, 0x1f44, 0x0008, 0x1fcd, 0x0006, 0x03f2, 0x0015, 0x1fbb, 0x014e, 0x0587, 0x0000, 0xffe4, 0x0021, 0x0000, 0x0000, 0xfff0, 0x000f, 0x2170, 0x000a, 0x0238, 0x0346, 0x0587, 0x0000, 0xffe6, 0x0019, 0x24d0, 0x0746, 0x0587, 0x0026, 0x0561, 0x000b, 0x057e, 0x0004, 0x012f, 0x0000, 0xd5d5, 0x0000, 0xd5d8, 0x000c, 0x022e, 0x000e, 0x03f8, 0x006e, 0x1e33, 0x0011, 0x0000, 0x0000, 0xe3a0, 0x0025, 0x2d00, 0x17f2, 0x0587, 0x6129, 0x2d26, 0x002e, 0x0201, 0x002a, 0x1def, 0x0098, 0xa5b7, 0x0040, 0x1dff, 0x000e, 0x0368, 0x000d, 0x022b, 0x034c, 0x2184, 0x5469, 0x2d26, 0x007f, 0x0061, 0x0040, 0x0000, }; /* Simple LZ decoder */ const u16 *in_next = upcase_compressed; for (u32 i = 0; i < ARRAY_LEN(upcase); ) { u16 length = *in_next++; u16 src_pos = *in_next++; if (length == 0) { /* Literal */ upcase[i++] = src_pos; } else { /* Match */ do { upcase[i++] = upcase[src_pos++]; } while (--length); } } /* Delta filter */ for (u32 i = 0; i < ARRAY_LEN(upcase); i++) upcase[i] += i; } /* * Compare UTF-16LE strings case-sensitively (%ignore_case == false) or * case-insensitively (%ignore_case == true). * * This is implemented using the default upper-case table used by NTFS. It does * not handle all possible cases allowed by UTF-16LE. For example, different * normalizations of the same sequence of "characters" are not considered equal. * It hopefully does the right thing most of the time though. */ int cmp_utf16le_strings(const utf16lechar *s1, size_t n1, const utf16lechar *s2, size_t n2, bool ignore_case) { size_t n = min(n1, n2); if (ignore_case) { for (size_t i = 0; i < n; i++) { u16 c1 = upcase[le16_to_cpu(s1[i])]; u16 c2 = upcase[le16_to_cpu(s2[i])]; if (c1 != c2) return (c1 < c2) ? -1 : 1; } } else { for (size_t i = 0; i < n; i++) { u16 c1 = le16_to_cpu(s1[i]); u16 c2 = le16_to_cpu(s2[i]); if (c1 != c2) return (c1 < c2) ? -1 : 1; } } if (n1 == n2) return 0; return (n1 < n2) ? -1 : 1; } /* Like cmp_utf16le_strings(), but assumes the strings are null terminated. */ int cmp_utf16le_strings_z(const utf16lechar *s1, const utf16lechar *s2, bool ignore_case) { if (ignore_case) { for (;;) { u16 c1 = upcase[le16_to_cpu(*s1)]; u16 c2 = upcase[le16_to_cpu(*s2)]; if (c1 != c2) return (c1 < c2) ? -1 : 1; if (c1 == 0) return 0; s1++, s2++; } } else { while (*s1 && *s1 == *s2) s1++, s2++; if (*s1 == *s2) return 0; return (le16_to_cpu(*s1) < le16_to_cpu(*s2)) ? -1 : 1; } } /* Duplicate a UTF-16 string. The input string might not be null terminated and * might be misaligned, but the returned string is guaranteed to be null * terminated and properly aligned. */ utf16lechar * utf16le_dupz(const void *s, size_t size) { utf16lechar *dup = MALLOC(size + sizeof(utf16lechar)); if (dup) { memcpy(dup, s, size); dup[size / sizeof(utf16lechar)] = 0; } return dup; } /* Duplicate a null-terminated UTF-16 string. */ utf16lechar * utf16le_dup(const utf16lechar *s) { return memdup(s, utf16le_len_bytes(s) + sizeof(utf16lechar)); } /* Return the length, in bytes, of a null terminated UTF-16 string, excluding * the null terminator. */ size_t utf16le_len_bytes(const utf16lechar *s) { const utf16lechar *p = s; while (*p) p++; return (p - s) * sizeof(utf16lechar); } /* Return the length, in UTF-16 coding units, of a null terminated UTF-16 * string, excluding the null terminator. */ size_t utf16le_len_chars(const utf16lechar *s) { return utf16le_len_bytes(s) / sizeof(utf16lechar); } wimlib-1.13.1/src/reference.c0000644000175000017500000001363413160354225012702 00000000000000/* * reference.c * * Reference blobs from external WIM file(s). */ /* * Copyright (C) 2013, 2014, 2015 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib.h" #include "wimlib/blob_table.h" #include "wimlib/error.h" #include "wimlib/glob.h" #include "wimlib/wim.h" #define WIMLIB_REF_MASK_PUBLIC (WIMLIB_REF_FLAG_GLOB_ENABLE | \ WIMLIB_REF_FLAG_GLOB_ERR_ON_NOMATCH) struct reference_info { WIMStruct *dest_wim; struct list_head new_blobs; int ref_flags; struct blob_table *src_table; }; static void init_reference_info(struct reference_info *info, WIMStruct *dest_wim, int ref_flags) { info->dest_wim = dest_wim; INIT_LIST_HEAD(&info->new_blobs); info->ref_flags = ref_flags; } static void rollback_reference_info(struct reference_info *info) { struct blob_descriptor *blob; while (!list_empty(&info->new_blobs)) { blob = list_first_entry(&info->new_blobs, struct blob_descriptor, blob_table_list); list_del(&blob->blob_table_list); blob_table_unlink(info->dest_wim->blob_table, blob); free_blob_descriptor(blob); } } static bool need_blob(const struct reference_info *info, const struct blob_descriptor *blob) { return !lookup_blob(info->dest_wim->blob_table, blob->hash); } static void reference_blob(struct reference_info *info, struct blob_descriptor *blob) { blob_table_insert(info->dest_wim->blob_table, blob); list_add(&blob->blob_table_list, &info->new_blobs); } static int blob_clone_if_new(struct blob_descriptor *blob, void *_info) { struct reference_info *info = _info; if (need_blob(info, blob)) { blob = clone_blob_descriptor(blob); if (unlikely(!blob)) return WIMLIB_ERR_NOMEM; reference_blob(info, blob); } return 0; } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_reference_resources(WIMStruct *wim, WIMStruct **resource_wims, unsigned num_resource_wims, int ref_flags) { unsigned i; struct reference_info info; int ret = 0; if (wim == NULL) return WIMLIB_ERR_INVALID_PARAM; if (num_resource_wims != 0 && resource_wims == NULL) return WIMLIB_ERR_INVALID_PARAM; if (ref_flags & ~WIMLIB_REF_MASK_PUBLIC) return WIMLIB_ERR_INVALID_PARAM; for (i = 0; i < num_resource_wims; i++) if (resource_wims[i] == NULL) return WIMLIB_ERR_INVALID_PARAM; init_reference_info(&info, wim, ref_flags); for (i = 0; i < num_resource_wims; i++) { ret = for_blob_in_table(resource_wims[i]->blob_table, blob_clone_if_new, &info); if (ret) break; } if (unlikely(ret)) rollback_reference_info(&info); return ret; } static int blob_gift(struct blob_descriptor *blob, void *_info) { struct reference_info *info = _info; blob_table_unlink(info->src_table, blob); if (need_blob(info, blob)) reference_blob(info, blob); else free_blob_descriptor(blob); return 0; } static int reference_resource_path(struct reference_info *info, const tchar *path, int open_flags) { int ret; WIMStruct *src_wim; ret = wimlib_open_wim_with_progress(path, open_flags, &src_wim, info->dest_wim->progfunc, info->dest_wim->progctx); if (ret) return ret; info->src_table = src_wim->blob_table; for_blob_in_table(src_wim->blob_table, blob_gift, info); wimlib_free(src_wim); return 0; } static int reference_resource_paths(struct reference_info *info, const tchar * const *paths, unsigned num_paths, int open_flags) { for (unsigned i = 0; i < num_paths; i++) { int ret = reference_resource_path(info, paths[i], open_flags); if (ret) return ret; } return 0; } static int reference_resource_glob(struct reference_info *info, const tchar *refglob, int open_flags) { int ret; glob_t globbuf; /* Note: glob() is replaced in Windows native builds. */ ret = tglob(refglob, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf); if (unlikely(ret)) { if (ret == GLOB_NOMATCH) { if (info->ref_flags & WIMLIB_REF_FLAG_GLOB_ERR_ON_NOMATCH) { ERROR("Found no files for glob \"%"TS"\"", refglob); return WIMLIB_ERR_GLOB_HAD_NO_MATCHES; } return reference_resource_path(info, refglob, open_flags); } ERROR_WITH_ERRNO("Failed to process glob \"%"TS"\"", refglob); if (ret == GLOB_NOSPACE) return WIMLIB_ERR_NOMEM; return WIMLIB_ERR_READ; } ret = reference_resource_paths(info, (const tchar * const *)globbuf.gl_pathv, globbuf.gl_pathc, open_flags); globfree(&globbuf); return ret; } static int reference_resource_globs(struct reference_info *info, const tchar * const *globs, unsigned num_globs, int open_flags) { for (unsigned i = 0; i < num_globs; i++) { int ret = reference_resource_glob(info, globs[i], open_flags); if (ret) return ret; } return 0; } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_reference_resource_files(WIMStruct *wim, const tchar * const *paths_or_globs, unsigned count, int ref_flags, int open_flags) { struct reference_info info; int ret; if (ref_flags & ~WIMLIB_REF_MASK_PUBLIC) return WIMLIB_ERR_INVALID_PARAM; init_reference_info(&info, wim, ref_flags); if (ref_flags & WIMLIB_REF_FLAG_GLOB_ENABLE) ret = reference_resource_globs(&info, paths_or_globs, count, open_flags); else ret = reference_resource_paths(&info, paths_or_globs, count, open_flags); if (unlikely(ret)) rollback_reference_info(&info); return ret; } wimlib-1.13.1/src/ntfs-3g_apply.c0000644000175000017500000007176113272231267013443 00000000000000/* * ntfs-3g_apply.c * * Apply a WIM image directly to an NTFS volume using libntfs-3g. Restore as * much information as possible, including security data, file attributes, DOS * names, alternate data streams, and object IDs. * * Note: because NTFS-3G offers inode-based interfaces, we actually don't need * to deal with paths at all! (Other than for error messages.) */ /* * Copyright (C) 2012-2017 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #include #include "wimlib/assert.h" #include "wimlib/apply.h" #include "wimlib/blob_table.h" #include "wimlib/dentry.h" #include "wimlib/encoding.h" #include "wimlib/error.h" #include "wimlib/metadata.h" #include "wimlib/ntfs_3g.h" #include "wimlib/object_id.h" #include "wimlib/reparse.h" #include "wimlib/security.h" static int ntfs_3g_get_supported_features(const char *target, struct wim_features *supported_features) { supported_features->readonly_files = 1; supported_features->hidden_files = 1; supported_features->system_files = 1; supported_features->archive_files = 1; supported_features->compressed_files = 1; supported_features->not_context_indexed_files = 1; supported_features->sparse_files = 1; supported_features->named_data_streams = 1; supported_features->hard_links = 1; supported_features->reparse_points = 1; supported_features->security_descriptors = 1; supported_features->short_names = 1; supported_features->object_ids = 1; supported_features->timestamps = 1; supported_features->case_sensitive_filenames = 1; return 0; } struct ntfs_3g_apply_ctx { /* Extract flags, the pointer to the WIMStruct, etc. */ struct apply_ctx common; /* Pointer to the open NTFS volume */ ntfs_volume *vol; ntfs_attr *open_attrs[MAX_OPEN_FILES]; unsigned num_open_attrs; ntfs_inode *open_inodes[MAX_OPEN_FILES]; unsigned num_open_inodes; /* For each currently open attribute, whether we're writing to it in * "sparse" mode or not. */ bool is_sparse_attr[MAX_OPEN_FILES]; /* Whether is_sparse_attr[] is true for any currently open attribute */ bool any_sparse_attrs; struct reparse_buffer_disk rpbuf; u8 *reparse_ptr; unsigned num_reparse_inodes; ntfs_inode *ntfs_reparse_inodes[MAX_OPEN_FILES]; struct wim_inode *wim_reparse_inodes[MAX_OPEN_FILES]; }; static int ntfs_3g_set_timestamps(ntfs_inode *ni, const struct wim_inode *inode) { u64 times[3] = { inode->i_creation_time, inode->i_last_write_time, inode->i_last_access_time, }; if (ntfs_inode_set_times(ni, (const char *)times, sizeof(times), 0)) return WIMLIB_ERR_SET_TIMESTAMPS; return 0; } /* Restore the timestamps on the NTFS inode corresponding to @inode. */ static int ntfs_3g_restore_timestamps(ntfs_volume *vol, const struct wim_inode *inode) { ntfs_inode *ni; int res; ni = ntfs_inode_open(vol, inode->i_mft_no); if (!ni) goto fail; res = ntfs_3g_set_timestamps(ni, inode); if (ntfs_inode_close(ni) || res) goto fail; return 0; fail: ERROR_WITH_ERRNO("Failed to update timestamps of \"%s\" in NTFS volume", dentry_full_path(inode_first_extraction_dentry(inode))); return WIMLIB_ERR_SET_TIMESTAMPS; } /* Restore the DOS name of the @dentry. * This closes both @ni and @dir_ni. * If either is NULL, then they are opened temporarily. */ static int ntfs_3g_restore_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, struct wim_dentry *dentry, ntfs_volume *vol) { int ret; const char *dos_name; size_t dos_name_nbytes; /* Note: ntfs_set_ntfs_dos_name() closes both inodes (even if it fails). * And it takes in a multibyte string, even though it translates it to * UTF-16LE internally... which is annoying because we currently have * the UTF-16LE string but not the multibyte string. */ ret = utf16le_get_tstr(dentry->d_short_name, dentry->d_short_name_nbytes, &dos_name, &dos_name_nbytes); if (ret) goto out_close; if (!dir_ni) dir_ni = ntfs_inode_open(vol, dentry->d_parent->d_inode->i_mft_no); if (!ni) ni = ntfs_inode_open(vol, dentry->d_inode->i_mft_no); if (dir_ni && ni) { ret = ntfs_set_ntfs_dos_name(ni, dir_ni, dos_name, dos_name_nbytes, 0); dir_ni = NULL; ni = NULL; } else { ret = -1; } utf16le_put_tstr(dos_name); if (unlikely(ret)) { int err = errno; ERROR_WITH_ERRNO("Failed to set DOS name of \"%s\" in NTFS " "volume", dentry_full_path(dentry)); if (err == EILSEQ) { ERROR("This error may have been caused by a known " "bug in libntfs-3g where it is unable to set " "DOS names on files whose long names contain " "unpaired surrogate characters. This bug " "was fixed in NTFS-3G version 2017.3.23."); } if (err == EINVAL) { utf16lechar c = dentry->d_name[dentry->d_name_nbytes / 2 - 1]; if (c == cpu_to_le16('.') || c == cpu_to_le16(' ')) { ERROR("This error was probably caused by a " "known bug in libntfs-3g where it is " "unable to set DOS names on files whose " "long names end with a dot or space " "character. This bug was fixed in " "NTFS-3G version 2017.3.23."); } } ret = WIMLIB_ERR_SET_SHORT_NAME; goto out_close; } /* Unlike most other NTFS-3G functions, ntfs_set_ntfs_dos_name() * changes the directory's last modification timestamp... * Change it back. */ return ntfs_3g_restore_timestamps(vol, dentry->d_parent->d_inode); out_close: /* ntfs_inode_close() can take a NULL argument, but it's probably best * not to rely on this behavior. */ if (ni) ntfs_inode_close(ni); if (dir_ni) ntfs_inode_close(dir_ni); return ret; } static int ntfs_3g_restore_reparse_point(ntfs_inode *ni, const struct wim_inode *inode, unsigned blob_size, struct ntfs_3g_apply_ctx *ctx) { complete_reparse_point(&ctx->rpbuf, inode, blob_size); if (ntfs_set_ntfs_reparse_data(ni, (const char *)&ctx->rpbuf, REPARSE_DATA_OFFSET + blob_size, 0)) { int err = errno; ERROR_WITH_ERRNO("Failed to set reparse data on \"%s\"", dentry_full_path( inode_first_extraction_dentry(inode))); if (err == EINVAL && !(inode->i_reparse_tag & 0x80000000)) { WARNING("This reparse point had a non-Microsoft reparse " "tag. The preceding error may have been caused " "by a known bug in libntfs-3g where it does not " "correctly validate non-Microsoft reparse " "points. This bug was fixed in NTFS-3G version " "2016.2.22."); } return WIMLIB_ERR_SET_REPARSE_DATA; } return 0; } static bool ntfs_3g_has_empty_attributes(const struct wim_inode *inode) { for (unsigned i = 0; i < inode->i_num_streams; i++) { const struct wim_inode_stream *strm = &inode->i_streams[i]; if (stream_blob_resolved(strm) == NULL && (strm->stream_type == STREAM_TYPE_REPARSE_POINT || stream_is_named_data_stream(strm))) return true; } return false; } /* * Create empty attributes (named data streams and potentially a reparse point) * for the specified file, if there are any. * * Since these won't have blob descriptors, they won't show up in the call to * extract_blob_list(). Hence the need for the special case. * * Keep this in sync with ntfs_3g_has_empty_attributes()! */ static int ntfs_3g_create_empty_attributes(ntfs_inode *ni, const struct wim_inode *inode, struct ntfs_3g_apply_ctx *ctx) { for (unsigned i = 0; i < inode->i_num_streams; i++) { const struct wim_inode_stream *strm = &inode->i_streams[i]; int ret; if (stream_blob_resolved(strm) != NULL) continue; if (strm->stream_type == STREAM_TYPE_REPARSE_POINT) { ret = ntfs_3g_restore_reparse_point(ni, inode, 0, ctx); if (ret) return ret; } else if (stream_is_named_data_stream(strm)) { if (ntfs_attr_add(ni, AT_DATA, strm->stream_name, utf16le_len_chars(strm->stream_name), NULL, 0)) { ERROR_WITH_ERRNO("Failed to create named data " "stream of \"%s\"", dentry_full_path( inode_first_extraction_dentry(inode))); return WIMLIB_ERR_NTFS_3G; } } } return 0; } /* Set attributes, security descriptor, and timestamps on the NTFS inode @ni. */ static int ntfs_3g_set_metadata(ntfs_inode *ni, const struct wim_inode *inode, const struct ntfs_3g_apply_ctx *ctx) { int extract_flags; const struct wim_security_data *sd; struct wim_dentry *one_dentry; int ret; extract_flags = ctx->common.extract_flags; sd = wim_get_current_security_data(ctx->common.wim); one_dentry = inode_first_extraction_dentry(inode); /* Object ID */ { u32 len; const void *object_id = inode_get_object_id(inode, &len); if (unlikely(object_id != NULL) && ntfs_set_ntfs_object_id(ni, object_id, len, 0)) { if (errno == EEXIST) { WARNING("Duplicate object ID on file \"%s\"", dentry_full_path(one_dentry)); } else { ERROR_WITH_ERRNO("Failed to set object ID on " "\"%s\" in NTFS volume", dentry_full_path(one_dentry)); return WIMLIB_ERR_NTFS_3G; } } } /* Attributes */ if (!(extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES)) { u32 attrib = inode->i_attributes; if (ntfs_set_ntfs_attrib(ni, (const char *)&attrib, sizeof(attrib), 0)) { ERROR_WITH_ERRNO("Failed to set attributes on \"%s\" " "in NTFS volume", dentry_full_path(one_dentry)); return WIMLIB_ERR_SET_ATTRIBUTES; } } /* Security descriptor */ if (inode_has_security_descriptor(inode) && !(extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS)) { struct SECURITY_CONTEXT sec_ctx = { ctx->vol }; const void *desc; size_t desc_size; desc = sd->descriptors[inode->i_security_id]; desc_size = sd->sizes[inode->i_security_id]; ret = ntfs_set_ntfs_acl(&sec_ctx, ni, desc, desc_size, 0); if (unlikely(ret)) { int err = errno; ERROR_WITH_ERRNO("Failed to set security descriptor on " "\"%s\" in NTFS volume", dentry_full_path(one_dentry)); if (err == EINVAL && wimlib_print_errors) { fprintf(wimlib_error_file, "The security descriptor is: "); print_byte_field(desc, desc_size, wimlib_error_file); fprintf(wimlib_error_file, "\n\nThis error occurred because libntfs-3g thinks " "the security descriptor is invalid. There " "are several known bugs with libntfs-3g's " "security descriptor validation logic in older " "versions. Please upgrade to NTFS-3G version " "2016.2.22 or later if you haven't already.\n"); } return WIMLIB_ERR_SET_SECURITY; } } /* Timestamps */ ret = ntfs_3g_set_timestamps(ni, inode); if (ret) { ERROR_WITH_ERRNO("Failed to set timestamps on \"%s\" " "in NTFS volume", dentry_full_path(one_dentry)); return ret; } return 0; } /* Recursively creates all the subdirectories of @dir, which has been created as * the NTFS inode @dir_ni. */ static int ntfs_3g_create_dirs_recursive(ntfs_inode *dir_ni, struct wim_dentry *dir, struct ntfs_3g_apply_ctx *ctx) { struct wim_dentry *child; for_dentry_child(child, dir) { ntfs_inode *ni; int ret; if (!(child->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)) continue; if (!will_extract_dentry(child)) continue; ni = ntfs_create(dir_ni, 0, child->d_extraction_name, child->d_extraction_name_nchars, S_IFDIR); if (!ni) { ERROR_WITH_ERRNO("Error creating \"%s\" in NTFS volume", dentry_full_path(child)); return WIMLIB_ERR_NTFS_3G; } child->d_inode->i_mft_no = ni->mft_no; ret = report_file_created(&ctx->common); if (!ret) ret = ntfs_3g_set_metadata(ni, child->d_inode, ctx); if (!ret) ret = ntfs_3g_create_dirs_recursive(ni, child, ctx); if (ntfs_inode_close_in_dir(ni, dir_ni) && !ret) { ERROR_WITH_ERRNO("Error closing \"%s\" in NTFS volume", dentry_full_path(child)); ret = WIMLIB_ERR_NTFS_3G; } if (ret) return ret; } return 0; } /* For each WIM dentry in the @root tree that represents a directory, create the * corresponding directory in the NTFS volume @ctx->vol. */ static int ntfs_3g_create_directories(struct wim_dentry *root, struct list_head *dentry_list, struct ntfs_3g_apply_ctx *ctx) { ntfs_inode *root_ni; int ret; struct wim_dentry *dentry; /* Create the directories using POSIX names. */ root_ni = ntfs_inode_open(ctx->vol, FILE_root); if (!root_ni) { ERROR_WITH_ERRNO("Can't open root of NTFS volume"); return WIMLIB_ERR_NTFS_3G; } root->d_inode->i_mft_no = FILE_root; ret = ntfs_3g_set_metadata(root_ni, root->d_inode, ctx); if (!ret) ret = ntfs_3g_create_dirs_recursive(root_ni, root, ctx); if (ntfs_inode_close(root_ni) && !ret) { ERROR_WITH_ERRNO("Error closing root of NTFS volume"); ret = WIMLIB_ERR_NTFS_3G; } if (ret) return ret; /* Set the DOS name of any directory that has one. In addition, create * empty attributes for directories that have them. Note that creating * an empty reparse point attribute must happen *after* setting the DOS * name in order to work around a case where ntfs_set_ntfs_dos_name() * fails with EOPNOTSUPP. This bug was fixed in NTFS-3G version * 2016.2.22. */ list_for_each_entry(dentry, dentry_list, d_extraction_list_node) { const struct wim_inode *inode = dentry->d_inode; if (!(inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)) continue; if (dentry_has_short_name(dentry)) { ret = ntfs_3g_restore_dos_name(NULL, NULL, dentry, ctx->vol); if (ret) return ret; ret = report_file_created(&ctx->common); if (ret) return ret; } if (ntfs_3g_has_empty_attributes(inode)) { ntfs_inode *ni; ret = WIMLIB_ERR_NTFS_3G; ni = ntfs_inode_open(ctx->vol, inode->i_mft_no); if (ni) { ret = ntfs_3g_create_empty_attributes(ni, inode, ctx); if (ntfs_inode_close(ni) && !ret) ret = WIMLIB_ERR_NTFS_3G; } if (ret) { ERROR_WITH_ERRNO("Failed to create empty " "attributes of directory " "\"%s\" in NTFS volume", dentry_full_path(dentry)); return ret; } } } return 0; } /* When creating an inode that will have a short (DOS) name, we create it using * the long name associated with the short name. This ensures that the short * name gets associated with the correct long name. */ static struct wim_dentry * ntfs_3g_first_extraction_alias(struct wim_inode *inode) { struct wim_dentry *dentry; inode_for_each_extraction_alias(dentry, inode) if (dentry_has_short_name(dentry)) return dentry; return inode_first_extraction_dentry(inode); } /* * Add a hard link for the NTFS inode @ni at the location corresponding to the * WIM dentry @dentry. * * The parent directory must have already been created on the NTFS volume. * * Returns 0 on success; returns WIMLIB_ERR_NTFS_3G and sets errno on failure. */ static int ntfs_3g_add_link(ntfs_inode *ni, struct wim_dentry *dentry) { ntfs_inode *dir_ni; int res; /* Open the inode of the parent directory. */ dir_ni = ntfs_inode_open(ni->vol, dentry->d_parent->d_inode->i_mft_no); if (!dir_ni) goto fail; /* Create the link. */ res = ntfs_link(ni, dir_ni, dentry->d_extraction_name, dentry->d_extraction_name_nchars); /* Close the parent directory. */ if (ntfs_inode_close(dir_ni) || res) goto fail; return 0; fail: ERROR_WITH_ERRNO("Can't create link \"%s\" in NTFS volume", dentry_full_path(dentry)); return WIMLIB_ERR_NTFS_3G; } static int ntfs_3g_create_nondirectory(struct wim_inode *inode, struct ntfs_3g_apply_ctx *ctx) { struct wim_dentry *first_dentry; ntfs_inode *dir_ni; ntfs_inode *ni; struct wim_dentry *dentry; int ret; first_dentry = ntfs_3g_first_extraction_alias(inode); /* Create first link. */ dir_ni = ntfs_inode_open(ctx->vol, first_dentry->d_parent->d_inode->i_mft_no); if (!dir_ni) { ERROR_WITH_ERRNO("Can't open \"%s\" in NTFS volume", dentry_full_path(first_dentry->d_parent)); return WIMLIB_ERR_NTFS_3G; } ni = ntfs_create(dir_ni, 0, first_dentry->d_extraction_name, first_dentry->d_extraction_name_nchars, S_IFREG); if (!ni) { ERROR_WITH_ERRNO("Can't create \"%s\" in NTFS volume", dentry_full_path(first_dentry)); ntfs_inode_close(dir_ni); return WIMLIB_ERR_NTFS_3G; } inode->i_mft_no = ni->mft_no; /* Set short name if present. */ if (dentry_has_short_name(first_dentry)) { ret = ntfs_3g_restore_dos_name(ni, dir_ni, first_dentry, ctx->vol); /* ntfs_3g_restore_dos_name() closed both 'ni' and 'dir_ni'. */ if (ret) return ret; /* Reopen the inode. */ ni = ntfs_inode_open(ctx->vol, inode->i_mft_no); if (!ni) { ERROR_WITH_ERRNO("Failed to reopen \"%s\" " "in NTFS volume", dentry_full_path(first_dentry)); return WIMLIB_ERR_NTFS_3G; } } else { /* Close the directory in which the first link was created. */ if (ntfs_inode_close(dir_ni)) { ERROR_WITH_ERRNO("Failed to close \"%s\" in NTFS volume", dentry_full_path(first_dentry->d_parent)); ret = WIMLIB_ERR_NTFS_3G; goto out_close_ni; } } /* Create additional links if present. */ inode_for_each_extraction_alias(dentry, inode) { if (dentry != first_dentry) { ret = ntfs_3g_add_link(ni, dentry); if (ret) goto out_close_ni; } } /* Set metadata. */ ret = ntfs_3g_set_metadata(ni, inode, ctx); if (ret) goto out_close_ni; ret = ntfs_3g_create_empty_attributes(ni, inode, ctx); out_close_ni: /* Close the inode. */ if (ntfs_inode_close(ni) && !ret) { ERROR_WITH_ERRNO("Error closing \"%s\" in NTFS volume", dentry_full_path(first_dentry)); ret = WIMLIB_ERR_NTFS_3G; } return ret; } /* For each WIM dentry in the @dentry_list that represents a nondirectory file, * create the corresponding nondirectory file in the NTFS volume. * * Directories must have already been created. */ static int ntfs_3g_create_nondirectories(struct list_head *dentry_list, struct ntfs_3g_apply_ctx *ctx) { struct wim_dentry *dentry; struct wim_inode *inode; int ret; list_for_each_entry(dentry, dentry_list, d_extraction_list_node) { inode = dentry->d_inode; if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) continue; if (dentry == inode_first_extraction_dentry(inode)) { ret = ntfs_3g_create_nondirectory(inode, ctx); if (ret) return ret; } ret = report_file_created(&ctx->common); if (ret) return ret; } return 0; } static int ntfs_3g_begin_extract_blob_instance(struct blob_descriptor *blob, ntfs_inode *ni, struct wim_inode *inode, const struct wim_inode_stream *strm, struct ntfs_3g_apply_ctx *ctx) { struct wim_dentry *one_dentry = inode_first_extraction_dentry(inode); ntfschar *stream_name; size_t stream_name_nchars; ntfs_attr *na; if (unlikely(strm->stream_type == STREAM_TYPE_REPARSE_POINT)) { if (blob->size > REPARSE_DATA_MAX_SIZE) { ERROR("Reparse data of \"%s\" has size " "%"PRIu64" bytes (exceeds %u bytes)", dentry_full_path(one_dentry), blob->size, REPARSE_DATA_MAX_SIZE); return WIMLIB_ERR_INVALID_REPARSE_DATA; } ctx->reparse_ptr = ctx->rpbuf.rpdata; ctx->ntfs_reparse_inodes[ctx->num_reparse_inodes] = ni; ctx->wim_reparse_inodes[ctx->num_reparse_inodes] = inode; ctx->num_reparse_inodes++; return 0; } /* It's a data stream (may be unnamed or named). */ wimlib_assert(strm->stream_type == STREAM_TYPE_DATA); if (unlikely(stream_is_named(strm))) { stream_name = strm->stream_name; stream_name_nchars = utf16le_len_chars(stream_name); if (ntfs_attr_add(ni, AT_DATA, stream_name, stream_name_nchars, NULL, 0)) { ERROR_WITH_ERRNO("Failed to create named data stream of \"%s\"", dentry_full_path(one_dentry)); return WIMLIB_ERR_NTFS_3G; } } else { /* Don't pass an empty string other than AT_UNNAMED to * ntfs_attr_open() --- it violates assumptions made by * libntfs-3g. */ stream_name = AT_UNNAMED; stream_name_nchars = 0; } /* This should be ensured by extract_blob_list() */ wimlib_assert(ctx->num_open_attrs < MAX_OPEN_FILES); na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_nchars); if (!na) { ERROR_WITH_ERRNO("Failed to open data stream of \"%s\"", dentry_full_path(one_dentry)); return WIMLIB_ERR_NTFS_3G; } /* * Note: there are problems with trying to combine compression with * sparseness when extracting. For example, doing ntfs_attr_truncate() * at the end to extend the attribute to its final size actually extends * to a compression block size boundary rather than to the requested * size. Until these problems are solved, we always write the full data * to compressed attributes. We also don't attempt to preallocate space * for compressed attributes, since we don't know how much space they * are going to actually need. */ ctx->is_sparse_attr[ctx->num_open_attrs] = false; if (!(na->data_flags & ATTR_COMPRESSION_MASK)) { if (inode->i_attributes & FILE_ATTRIBUTE_SPARSE_FILE) { ctx->is_sparse_attr[ctx->num_open_attrs] = true; ctx->any_sparse_attrs = true; } else { ntfs_attr_truncate_solid(na, blob->size); } } ctx->open_attrs[ctx->num_open_attrs++] = na; return 0; } static int ntfs_3g_cleanup_blob_extract(struct ntfs_3g_apply_ctx *ctx) { int ret = 0; for (unsigned i = 0; i < ctx->num_open_attrs; i++) { if (ntfs_attr_pclose(ctx->open_attrs[i])) ret = -1; ntfs_attr_close(ctx->open_attrs[i]); } ctx->num_open_attrs = 0; for (unsigned i = 0; i < ctx->num_open_inodes; i++) { if (ntfs_inode_close(ctx->open_inodes[i])) ret = -1; } ctx->num_open_inodes = 0; ctx->any_sparse_attrs = false; ctx->reparse_ptr = NULL; ctx->num_reparse_inodes = 0; return ret; } static ntfs_inode * ntfs_3g_open_inode(struct wim_inode *inode, struct ntfs_3g_apply_ctx *ctx) { ntfs_inode *ni; /* If the same blob is being extracted to multiple streams of the same * inode, then we must only open the inode once. */ if (unlikely(inode->i_num_streams > 1)) { for (unsigned i = 0; i < ctx->num_open_inodes; i++) { if (ctx->open_inodes[i]->mft_no == inode->i_mft_no) { return ctx->open_inodes[i]; } } } ni = ntfs_inode_open(ctx->vol, inode->i_mft_no); if (unlikely(!ni)) { ERROR_WITH_ERRNO("Can't open \"%s\" in NTFS volume", dentry_full_path( inode_first_extraction_dentry(inode))); return NULL; } ctx->open_inodes[ctx->num_open_inodes++] = ni; return ni; } static int ntfs_3g_begin_extract_blob(struct blob_descriptor *blob, void *_ctx) { struct ntfs_3g_apply_ctx *ctx = _ctx; const struct blob_extraction_target *targets = blob_extraction_targets(blob); int ret; ntfs_inode *ni; for (u32 i = 0; i < blob->out_refcnt; i++) { ret = WIMLIB_ERR_NTFS_3G; ni = ntfs_3g_open_inode(targets[i].inode, ctx); if (!ni) goto out_cleanup; ret = ntfs_3g_begin_extract_blob_instance(blob, ni, targets[i].inode, targets[i].stream, ctx); if (ret) goto out_cleanup; } ret = 0; goto out; out_cleanup: ntfs_3g_cleanup_blob_extract(ctx); out: return ret; } /* * Note: prior to NTFS-3G version 2016.2.22, ntfs_attr_pwrite() could return a * short count in non-error cases, contrary to its documentation. Specifically, * a short count could be returned when writing to a compressed attribute and * the requested count exceeded the size of an NTFS "compression block". * Therefore, we must continue calling ntfs_attr_pwrite() until all bytes have * been written or a real error has occurred. */ static bool ntfs_3g_full_pwrite(ntfs_attr *na, u64 offset, size_t size, const u8 *data) { while (size) { s64 res = ntfs_attr_pwrite(na, offset, size, data); if (unlikely(res <= 0)) return false; wimlib_assert(res <= size); offset += res; size -= res; data += res; } return true; } static int ntfs_3g_extract_chunk(const struct blob_descriptor *blob, u64 offset, const void *chunk, size_t size, void *_ctx) { struct ntfs_3g_apply_ctx *ctx = _ctx; const void * const end = chunk + size; const void *p; bool zeroes; size_t len; unsigned i; /* * For sparse attributes, only write nonzero regions. This lets the * filesystem use holes to represent zero regions. */ for (p = chunk; p != end; p += len, offset += len) { zeroes = maybe_detect_sparse_region(p, end - p, &len, ctx->any_sparse_attrs); for (i = 0; i < ctx->num_open_attrs; i++) { if (!zeroes || !ctx->is_sparse_attr[i]) { if (!ntfs_3g_full_pwrite(ctx->open_attrs[i], offset, len, p)) goto err; } } } if (ctx->reparse_ptr) ctx->reparse_ptr = mempcpy(ctx->reparse_ptr, chunk, size); return 0; err: ERROR_WITH_ERRNO("Error writing data to NTFS volume"); return WIMLIB_ERR_NTFS_3G; } static int ntfs_3g_end_extract_blob(struct blob_descriptor *blob, int status, void *_ctx) { struct ntfs_3g_apply_ctx *ctx = _ctx; int ret; if (status) { ret = status; goto out; } /* Extend sparse attributes to their final size. */ if (ctx->any_sparse_attrs) { for (unsigned i = 0; i < ctx->num_open_attrs; i++) { if (!ctx->is_sparse_attr[i]) continue; if (ntfs_attr_truncate(ctx->open_attrs[i], blob->size)) { ERROR_WITH_ERRNO("Error extending attribute to " "final size"); ret = WIMLIB_ERR_WRITE; goto out; } } } for (u32 i = 0; i < ctx->num_reparse_inodes; i++) { ret = ntfs_3g_restore_reparse_point(ctx->ntfs_reparse_inodes[i], ctx->wim_reparse_inodes[i], blob->size, ctx); if (ret) goto out; } ret = 0; out: if (ntfs_3g_cleanup_blob_extract(ctx) && !ret) { ERROR_WITH_ERRNO("Error writing data to NTFS volume"); ret = WIMLIB_ERR_NTFS_3G; } return ret; } static u64 ntfs_3g_count_dentries(const struct list_head *dentry_list) { const struct wim_dentry *dentry; u64 count = 0; list_for_each_entry(dentry, dentry_list, d_extraction_list_node) { count++; if ((dentry->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) && dentry_has_short_name(dentry)) { count++; } } return count; } static int ntfs_3g_extract(struct list_head *dentry_list, struct apply_ctx *_ctx) { struct ntfs_3g_apply_ctx *ctx = (struct ntfs_3g_apply_ctx *)_ctx; ntfs_volume *vol; struct wim_dentry *root; int ret; /* For NTFS-3G extraction mode we require that the dentries to extract * form a single tree. */ root = list_first_entry(dentry_list, struct wim_dentry, d_extraction_list_node); /* Mount the NTFS volume. */ vol = ntfs_mount(ctx->common.target, 0); if (!vol) { ERROR_WITH_ERRNO("Failed to mount \"%s\" with NTFS-3G", ctx->common.target); return WIMLIB_ERR_NTFS_3G; } ctx->vol = vol; /* Opening $Secure is required to set security descriptors in NTFS v3.0 * format, where security descriptors are stored in a per-volume index * rather than being fully specified for each file. */ if (ntfs_open_secure(vol) && vol->major_ver >= 3) { ERROR_WITH_ERRNO("Unable to open security descriptor index of " "NTFS volume \"%s\"", ctx->common.target); ret = WIMLIB_ERR_NTFS_3G; goto out_unmount; } /* Create all inodes and aliases, including short names, and set * metadata (attributes, security descriptors, and timestamps). */ ret = start_file_structure_phase(&ctx->common, ntfs_3g_count_dentries(dentry_list)); if (ret) goto out_unmount; ret = ntfs_3g_create_directories(root, dentry_list, ctx); if (ret) goto out_unmount; ret = ntfs_3g_create_nondirectories(dentry_list, ctx); if (ret) goto out_unmount; ret = end_file_structure_phase(&ctx->common); if (ret) goto out_unmount; /* Extract blobs. */ struct read_blob_callbacks cbs = { .begin_blob = ntfs_3g_begin_extract_blob, .continue_blob = ntfs_3g_extract_chunk, .end_blob = ntfs_3g_end_extract_blob, .ctx = ctx, }; ret = extract_blob_list(&ctx->common, &cbs); /* We do not need a final pass to set timestamps because libntfs-3g does * not update timestamps automatically (exception: * ntfs_set_ntfs_dos_name() does, but we handle this elsewhere). */ out_unmount: if (vol->secure_ni) { ntfs_index_ctx_put(vol->secure_xsii); ntfs_index_ctx_put(vol->secure_xsdh); if (ntfs_inode_close(vol->secure_ni) && !ret) { ERROR_WITH_ERRNO("Failed to close security descriptor " "index of NTFS volume \"%s\"", ctx->common.target); ret = WIMLIB_ERR_NTFS_3G; } vol->secure_ni = NULL; } if (ntfs_umount(ctx->vol, FALSE) && !ret) { ERROR_WITH_ERRNO("Failed to unmount \"%s\" with NTFS-3G", ctx->common.target); ret = WIMLIB_ERR_NTFS_3G; } return ret; } const struct apply_operations ntfs_3g_apply_ops = { .name = "NTFS-3G", .get_supported_features = ntfs_3g_get_supported_features, .extract = ntfs_3g_extract, .context_size = sizeof(struct ntfs_3g_apply_ctx), .single_tree_only = true, }; wimlib-1.13.1/src/lzx_decompress.c0000644000175000017500000004172013272231267014006 00000000000000/* * lzx_decompress.c * * A decompressor for the LZX compression format, as used in WIM files. */ /* * Copyright (C) 2012-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ /* * LZX is an LZ77 and Huffman-code based compression format that has many * similarities to DEFLATE (the format used by zlib/gzip). The compression * ratio is as good or better than DEFLATE. See lzx_compress.c for a format * overview, and see https://en.wikipedia.org/wiki/LZX_(algorithm) for a * historical overview. Here I make some pragmatic notes. * * The old specification for LZX is the document "Microsoft LZX Data Compression * Format" (1997). It defines the LZX format as used in cabinet files. Allowed * window sizes are 2^n where 15 <= n <= 21. However, this document contains * several errors, so don't read too much into it... * * The new specification for LZX is the document "[MS-PATCH]: LZX DELTA * Compression and Decompression" (2014). It defines the LZX format as used by * Microsoft's binary patcher. It corrects several errors in the 1997 document * and extends the format in several ways --- namely, optional reference data, * up to 2^25 byte windows, and longer match lengths. * * WIM files use a more restricted form of LZX. No LZX DELTA extensions are * present, the window is not "sliding", E8 preprocessing is done * unconditionally with a fixed file size, and the maximum window size is always * 2^15 bytes (equal to the size of each "chunk" in a compressed WIM resource). * This code is primarily intended to implement this form of LZX. But although * not compatible with WIMGAPI, this code also supports maximum window sizes up * to 2^21 bytes. * * TODO: Add support for window sizes up to 2^25 bytes. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include "wimlib/decompressor_ops.h" #include "wimlib/decompress_common.h" #include "wimlib/error.h" #include "wimlib/lzx_common.h" #include "wimlib/util.h" /* These values are chosen for fast decompression. */ #define LZX_MAINCODE_TABLEBITS 11 #define LZX_LENCODE_TABLEBITS 9 #define LZX_PRECODE_TABLEBITS 6 #define LZX_ALIGNEDCODE_TABLEBITS 7 #define LZX_READ_LENS_MAX_OVERRUN 50 struct lzx_decompressor { DECODE_TABLE(maincode_decode_table, LZX_MAINCODE_MAX_NUM_SYMBOLS, LZX_MAINCODE_TABLEBITS, LZX_MAX_MAIN_CODEWORD_LEN); u8 maincode_lens[LZX_MAINCODE_MAX_NUM_SYMBOLS + LZX_READ_LENS_MAX_OVERRUN]; DECODE_TABLE(lencode_decode_table, LZX_LENCODE_NUM_SYMBOLS, LZX_LENCODE_TABLEBITS, LZX_MAX_LEN_CODEWORD_LEN); u8 lencode_lens[LZX_LENCODE_NUM_SYMBOLS + LZX_READ_LENS_MAX_OVERRUN]; union { DECODE_TABLE(alignedcode_decode_table, LZX_ALIGNEDCODE_NUM_SYMBOLS, LZX_ALIGNEDCODE_TABLEBITS, LZX_MAX_ALIGNED_CODEWORD_LEN); u8 alignedcode_lens[LZX_ALIGNEDCODE_NUM_SYMBOLS]; }; union { DECODE_TABLE(precode_decode_table, LZX_PRECODE_NUM_SYMBOLS, LZX_PRECODE_TABLEBITS, LZX_MAX_PRE_CODEWORD_LEN); u8 precode_lens[LZX_PRECODE_NUM_SYMBOLS]; u8 extra_offset_bits[LZX_MAX_OFFSET_SLOTS]; }; union { DECODE_TABLE_WORKING_SPACE(maincode_working_space, LZX_MAINCODE_MAX_NUM_SYMBOLS, LZX_MAX_MAIN_CODEWORD_LEN); DECODE_TABLE_WORKING_SPACE(lencode_working_space, LZX_LENCODE_NUM_SYMBOLS, LZX_MAX_LEN_CODEWORD_LEN); DECODE_TABLE_WORKING_SPACE(alignedcode_working_space, LZX_ALIGNEDCODE_NUM_SYMBOLS, LZX_MAX_ALIGNED_CODEWORD_LEN); DECODE_TABLE_WORKING_SPACE(precode_working_space, LZX_PRECODE_NUM_SYMBOLS, LZX_MAX_PRE_CODEWORD_LEN); }; unsigned window_order; unsigned num_main_syms; /* Like lzx_extra_offset_bits[], but does not include the entropy-coded * bits of aligned offset blocks */ u8 extra_offset_bits_minus_aligned[LZX_MAX_OFFSET_SLOTS]; } _aligned_attribute(DECODE_TABLE_ALIGNMENT); /* Read a Huffman-encoded symbol using the precode. */ static forceinline unsigned read_presym(const struct lzx_decompressor *d, struct input_bitstream *is) { return read_huffsym(is, d->precode_decode_table, LZX_PRECODE_TABLEBITS, LZX_MAX_PRE_CODEWORD_LEN); } /* Read a Huffman-encoded symbol using the main code. */ static forceinline unsigned read_mainsym(const struct lzx_decompressor *d, struct input_bitstream *is) { return read_huffsym(is, d->maincode_decode_table, LZX_MAINCODE_TABLEBITS, LZX_MAX_MAIN_CODEWORD_LEN); } /* Read a Huffman-encoded symbol using the length code. */ static forceinline unsigned read_lensym(const struct lzx_decompressor *d, struct input_bitstream *is) { return read_huffsym(is, d->lencode_decode_table, LZX_LENCODE_TABLEBITS, LZX_MAX_LEN_CODEWORD_LEN); } /* Read a Huffman-encoded symbol using the aligned offset code. */ static forceinline unsigned read_alignedsym(const struct lzx_decompressor *d, struct input_bitstream *is) { return read_huffsym(is, d->alignedcode_decode_table, LZX_ALIGNEDCODE_TABLEBITS, LZX_MAX_ALIGNED_CODEWORD_LEN); } /* * Read a precode from the compressed input bitstream, then use it to decode * @num_lens codeword length values and write them to @lens. */ static int lzx_read_codeword_lens(struct lzx_decompressor *d, struct input_bitstream *is, u8 *lens, unsigned num_lens) { u8 *len_ptr = lens; u8 *lens_end = lens + num_lens; /* Read the lengths of the precode codewords. These are stored * explicitly. */ for (int i = 0; i < LZX_PRECODE_NUM_SYMBOLS; i++) { d->precode_lens[i] = bitstream_read_bits(is, LZX_PRECODE_ELEMENT_SIZE); } /* Build the decoding table for the precode. */ if (make_huffman_decode_table(d->precode_decode_table, LZX_PRECODE_NUM_SYMBOLS, LZX_PRECODE_TABLEBITS, d->precode_lens, LZX_MAX_PRE_CODEWORD_LEN, d->precode_working_space)) return -1; /* Decode the codeword lengths. */ do { unsigned presym; u8 len; /* Read the next precode symbol. */ presym = read_presym(d, is); if (presym < 17) { /* Difference from old length */ len = *len_ptr - presym; if ((s8)len < 0) len += 17; *len_ptr++ = len; } else { /* Special RLE values */ unsigned run_len; if (presym == 17) { /* Run of 0's */ run_len = 4 + bitstream_read_bits(is, 4); len = 0; } else if (presym == 18) { /* Longer run of 0's */ run_len = 20 + bitstream_read_bits(is, 5); len = 0; } else { /* Run of identical lengths */ run_len = 4 + bitstream_read_bits(is, 1); presym = read_presym(d, is); if (unlikely(presym > 17)) return -1; len = *len_ptr - presym; if ((s8)len < 0) len += 17; } do { *len_ptr++ = len; } while (--run_len); /* * The worst case overrun is when presym == 18, * run_len == 20 + 31, and only 1 length was remaining. * So LZX_READ_LENS_MAX_OVERRUN == 50. * * Overrun while reading the first half of maincode_lens * can corrupt the previous values in the second half. * This doesn't really matter because the resulting * lengths will still be in range, and data that * generates overruns is invalid anyway. */ } } while (len_ptr < lens_end); return 0; } /* * Read the header of an LZX block. For all block types, the block type and * size is saved in *block_type_ret and *block_size_ret, respectively. For * compressed blocks, the codeword lengths are also saved. For uncompressed * blocks, the recent offsets queue is also updated. */ static int lzx_read_block_header(struct lzx_decompressor *d, struct input_bitstream *is, u32 recent_offsets[], int *block_type_ret, u32 *block_size_ret) { int block_type; u32 block_size; bitstream_ensure_bits(is, 4); /* Read the block type. */ block_type = bitstream_pop_bits(is, 3); /* Read the block size. */ if (bitstream_pop_bits(is, 1)) { block_size = LZX_DEFAULT_BLOCK_SIZE; } else { block_size = bitstream_read_bits(is, 16); if (d->window_order >= 16) { block_size <<= 8; block_size |= bitstream_read_bits(is, 8); } } switch (block_type) { case LZX_BLOCKTYPE_ALIGNED: /* Read the aligned offset codeword lengths. */ for (int i = 0; i < LZX_ALIGNEDCODE_NUM_SYMBOLS; i++) { d->alignedcode_lens[i] = bitstream_read_bits(is, LZX_ALIGNEDCODE_ELEMENT_SIZE); } /* Fall though, since the rest of the header for aligned offset * blocks is the same as that for verbatim blocks. */ case LZX_BLOCKTYPE_VERBATIM: /* Read the main codeword lengths, which are divided into two * parts: literal symbols and match headers. */ if (lzx_read_codeword_lens(d, is, d->maincode_lens, LZX_NUM_CHARS)) return -1; if (lzx_read_codeword_lens(d, is, d->maincode_lens + LZX_NUM_CHARS, d->num_main_syms - LZX_NUM_CHARS)) return -1; /* Read the length codeword lengths. */ if (lzx_read_codeword_lens(d, is, d->lencode_lens, LZX_LENCODE_NUM_SYMBOLS)) return -1; break; case LZX_BLOCKTYPE_UNCOMPRESSED: /* * The header of an uncompressed block contains new values for * the recent offsets queue, starting on the next 16-bit * boundary in the bitstream. Careful: if the stream is * *already* aligned, the correct thing to do is to throw away * the next 16 bits (this is probably a mistake in the format). */ bitstream_ensure_bits(is, 1); bitstream_align(is); recent_offsets[0] = bitstream_read_u32(is); recent_offsets[1] = bitstream_read_u32(is); recent_offsets[2] = bitstream_read_u32(is); /* Offsets of 0 are invalid. */ if (recent_offsets[0] == 0 || recent_offsets[1] == 0 || recent_offsets[2] == 0) return -1; break; default: /* Unrecognized block type. */ return -1; } *block_type_ret = block_type; *block_size_ret = block_size; return 0; } /* Decompress a block of LZX-compressed data. */ static int lzx_decompress_block(struct lzx_decompressor *d, struct input_bitstream *_is, int block_type, u32 block_size, u8 * const out_begin, u8 *out_next, u32 recent_offsets[]) { /* * Redeclare the input bitstream on the stack. This shouldn't be * needed, but it can improve the main loop's performance significantly * with both gcc and clang, apparently because the compiler otherwise * gets confused and doesn't properly allocate registers for * 'is->bitbuf' et al. and/or thinks 'is->next' may point into 'is'. */ struct input_bitstream is_onstack = *_is; struct input_bitstream *is = &is_onstack; u8 * const block_end = out_next + block_size; unsigned min_aligned_offset_slot; /* * Build the Huffman decode tables. We always need to build the main * and length decode tables. For aligned blocks we additionally need to * build the aligned offset decode table. */ if (make_huffman_decode_table(d->maincode_decode_table, d->num_main_syms, LZX_MAINCODE_TABLEBITS, d->maincode_lens, LZX_MAX_MAIN_CODEWORD_LEN, d->maincode_working_space)) return -1; if (make_huffman_decode_table(d->lencode_decode_table, LZX_LENCODE_NUM_SYMBOLS, LZX_LENCODE_TABLEBITS, d->lencode_lens, LZX_MAX_LEN_CODEWORD_LEN, d->lencode_working_space)) return -1; if (block_type == LZX_BLOCKTYPE_ALIGNED) { if (make_huffman_decode_table(d->alignedcode_decode_table, LZX_ALIGNEDCODE_NUM_SYMBOLS, LZX_ALIGNEDCODE_TABLEBITS, d->alignedcode_lens, LZX_MAX_ALIGNED_CODEWORD_LEN, d->alignedcode_working_space)) return -1; min_aligned_offset_slot = LZX_MIN_ALIGNED_OFFSET_SLOT; memcpy(d->extra_offset_bits, d->extra_offset_bits_minus_aligned, sizeof(lzx_extra_offset_bits)); } else { min_aligned_offset_slot = LZX_MAX_OFFSET_SLOTS; memcpy(d->extra_offset_bits, lzx_extra_offset_bits, sizeof(lzx_extra_offset_bits)); } /* Decode the literals and matches. */ do { unsigned mainsym; unsigned length; u32 offset; unsigned offset_slot; mainsym = read_mainsym(d, is); if (mainsym < LZX_NUM_CHARS) { /* Literal */ *out_next++ = mainsym; continue; } /* Match */ /* Decode the length header and offset slot. */ STATIC_ASSERT(LZX_NUM_CHARS % LZX_NUM_LEN_HEADERS == 0); length = mainsym % LZX_NUM_LEN_HEADERS; offset_slot = (mainsym - LZX_NUM_CHARS) / LZX_NUM_LEN_HEADERS; /* If needed, read a length symbol to decode the full length. */ if (length == LZX_NUM_PRIMARY_LENS) length += read_lensym(d, is); length += LZX_MIN_MATCH_LEN; if (offset_slot < LZX_NUM_RECENT_OFFSETS) { /* Repeat offset */ /* Note: This isn't a real LRU queue, since using the R2 * offset doesn't bump the R1 offset down to R2. */ offset = recent_offsets[offset_slot]; recent_offsets[offset_slot] = recent_offsets[0]; } else { /* Explicit offset */ offset = bitstream_read_bits(is, d->extra_offset_bits[offset_slot]); if (offset_slot >= min_aligned_offset_slot) { offset = (offset << LZX_NUM_ALIGNED_OFFSET_BITS) | read_alignedsym(d, is); } offset += lzx_offset_slot_base[offset_slot]; /* Update the match offset LRU queue. */ STATIC_ASSERT(LZX_NUM_RECENT_OFFSETS == 3); recent_offsets[2] = recent_offsets[1]; recent_offsets[1] = recent_offsets[0]; } recent_offsets[0] = offset; /* Validate the match and copy it to the current position. */ if (unlikely(lz_copy(length, offset, out_begin, out_next, block_end, LZX_MIN_MATCH_LEN))) return -1; out_next += length; } while (out_next != block_end); *_is = is_onstack; return 0; } static int lzx_decompress(const void *restrict compressed_data, size_t compressed_size, void *restrict uncompressed_data, size_t uncompressed_size, void *restrict _d) { struct lzx_decompressor *d = _d; u8 * const out_begin = uncompressed_data; u8 *out_next = out_begin; u8 * const out_end = out_begin + uncompressed_size; struct input_bitstream is; STATIC_ASSERT(LZX_NUM_RECENT_OFFSETS == 3); u32 recent_offsets[LZX_NUM_RECENT_OFFSETS] = {1, 1, 1}; unsigned may_have_e8_byte = 0; init_input_bitstream(&is, compressed_data, compressed_size); /* Codeword lengths begin as all 0's for delta encoding purposes. */ memset(d->maincode_lens, 0, d->num_main_syms); memset(d->lencode_lens, 0, LZX_LENCODE_NUM_SYMBOLS); /* Decompress blocks until we have all the uncompressed data. */ while (out_next != out_end) { int block_type; u32 block_size; if (lzx_read_block_header(d, &is, recent_offsets, &block_type, &block_size)) return -1; if (block_size < 1 || block_size > out_end - out_next) return -1; if (likely(block_type != LZX_BLOCKTYPE_UNCOMPRESSED)) { /* Compressed block */ if (lzx_decompress_block(d, &is, block_type, block_size, out_begin, out_next, recent_offsets)) return -1; /* If the first E8 byte was in this block, then it must * have been encoded as a literal using mainsym E8. */ may_have_e8_byte |= d->maincode_lens[0xE8]; } else { /* Uncompressed block */ if (bitstream_read_bytes(&is, out_next, block_size)) return -1; /* Re-align the bitstream if needed. */ if (block_size & 1) bitstream_read_byte(&is); /* There may have been an E8 byte in the block. */ may_have_e8_byte = 1; } out_next += block_size; } /* Postprocess the data unless it cannot possibly contain E8 bytes. */ if (may_have_e8_byte) lzx_postprocess(uncompressed_data, uncompressed_size); return 0; } static int lzx_create_decompressor(size_t max_block_size, void **d_ret) { unsigned window_order; struct lzx_decompressor *d; window_order = lzx_get_window_order(max_block_size); if (window_order == 0) return WIMLIB_ERR_INVALID_PARAM; d = ALIGNED_MALLOC(sizeof(*d), DECODE_TABLE_ALIGNMENT); if (!d) return WIMLIB_ERR_NOMEM; d->window_order = window_order; d->num_main_syms = lzx_get_num_main_syms(window_order); /* Initialize 'd->extra_offset_bits_minus_aligned'. */ STATIC_ASSERT(sizeof(d->extra_offset_bits_minus_aligned) == sizeof(lzx_extra_offset_bits)); STATIC_ASSERT(sizeof(d->extra_offset_bits) == sizeof(lzx_extra_offset_bits)); memcpy(d->extra_offset_bits_minus_aligned, lzx_extra_offset_bits, sizeof(lzx_extra_offset_bits)); for (unsigned offset_slot = LZX_MIN_ALIGNED_OFFSET_SLOT; offset_slot < LZX_MAX_OFFSET_SLOTS; offset_slot++) { d->extra_offset_bits_minus_aligned[offset_slot] -= LZX_NUM_ALIGNED_OFFSET_BITS; } *d_ret = d; return 0; } static void lzx_free_decompressor(void *_d) { ALIGNED_FREE(_d); } const struct decompressor_ops lzx_decompressor_ops = { .create_decompressor = lzx_create_decompressor, .decompress = lzx_decompress, .free_decompressor = lzx_free_decompressor, }; wimlib-1.13.1/src/textfile.c0000644000175000017500000002232013324701534012561 00000000000000/* * textfile.c */ /* * Copyright (C) 2014 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #include "wimlib/encoding.h" #include "wimlib/error.h" #include "wimlib/file_io.h" #include "wimlib/textfile.h" #include "wimlib/util.h" static int stdin_get_contents(void **buf_ret, size_t *bufsize_ret) { char *buf = NULL; size_t filled = 0; size_t capacity = 0; do { size_t new_capacity = (capacity * 2) + 256; char *new_buf; if (new_capacity <= capacity || !(new_buf = REALLOC(buf, new_capacity))) { ERROR("Too much data sent on stdin!"); FREE(buf); return WIMLIB_ERR_INVALID_PARAM; } buf = new_buf; capacity = new_capacity; filled += fread(&buf[filled], 1, capacity - filled, stdin); } while (filled == capacity); if (!feof(stdin)) { ERROR_WITH_ERRNO("Error reading stdin"); FREE(buf); return WIMLIB_ERR_READ; } *buf_ret = buf; *bufsize_ret = filled; return 0; } static int read_file_contents(const tchar *path, void **buf_ret, size_t *bufsize_ret) { int raw_fd; struct filedes fd; struct stat st; void *buf; int ret; int errno_save; raw_fd = topen(path, O_RDONLY | O_BINARY); if (raw_fd < 0) { ERROR_WITH_ERRNO("Can't open \"%"TS"\"", path); return WIMLIB_ERR_OPEN; } if (fstat(raw_fd, &st)) { ERROR_WITH_ERRNO("Can't stat \"%"TS"\"", path); close(raw_fd); return WIMLIB_ERR_STAT; } if ((size_t)st.st_size != st.st_size || (buf = MALLOC(st.st_size)) == NULL) { close(raw_fd); ERROR("Not enough memory to read \"%"TS"\"", path); return WIMLIB_ERR_NOMEM; } filedes_init(&fd, raw_fd); ret = full_read(&fd, buf, st.st_size); errno_save = errno; filedes_close(&fd); errno = errno_save; if (ret) { ERROR_WITH_ERRNO("Error reading \"%"TS"\"", path); FREE(buf); return ret; } *buf_ret = buf; *bufsize_ret = st.st_size; return 0; } static int translate_text_buffer(const u8 *buf_raw, size_t bufsize_raw, tchar **tstr_ret, size_t *tstr_nchars_ret) { size_t offset_raw; bool utf8; tchar *buf_tstr; size_t bufsize_tstr; int ret; /* Guess the encoding: UTF-8 or UTF-16LE. (Something weirder and you're * out of luck, sorry...) */ if (bufsize_raw >= 2 && buf_raw[0] == 0xFF && buf_raw[1] == 0xFE) { utf8 = false; offset_raw = 2; } else if (bufsize_raw >= 2 && buf_raw[0] <= 0x7F && buf_raw[1] == 0x00) { utf8 = false; offset_raw = 0; } else if (bufsize_raw >= 3 && buf_raw[0] == 0xEF && buf_raw[1] == 0xBB && buf_raw[2] == 0xBF) { utf8 = true; offset_raw = 3; } else { utf8 = true; offset_raw = 0; } if (utf8) { ret = utf8_to_tstr((const char *)(buf_raw + offset_raw), bufsize_raw - offset_raw, &buf_tstr, &bufsize_tstr); } else { ret = utf16le_to_tstr((const utf16lechar *)(buf_raw + offset_raw), bufsize_raw - offset_raw, &buf_tstr, &bufsize_tstr); } if (ret) return ret; *tstr_ret = buf_tstr; *tstr_nchars_ret = bufsize_tstr / sizeof(tchar); return 0; } static int string_list_append(struct string_list *list, tchar *str) { size_t num_alloc_strings = list->num_alloc_strings; if (list->num_strings == num_alloc_strings) { tchar **new_strings; num_alloc_strings = max(num_alloc_strings * 3 / 2, num_alloc_strings + 4); new_strings = REALLOC(list->strings, sizeof(list->strings[0]) * num_alloc_strings); if (!new_strings) return WIMLIB_ERR_NOMEM; list->strings = new_strings; list->num_alloc_strings = num_alloc_strings; } list->strings[list->num_strings++] = str; return 0; } #define NOT_IN_SECTION -1 #define IN_UNKNOWN_SECTION -2 static int parse_text_file(const tchar *path, tchar *buf, size_t buflen, const struct text_file_section *pos_sections, int num_pos_sections, int flags, line_mangle_t mangle_line) { int current_section = NOT_IN_SECTION; bool have_named_sections = false; tchar *p; tchar *nl; unsigned long line_no = 1; for (int i = 0; i < num_pos_sections; i++) { if (*pos_sections[i].name) have_named_sections = true; else current_section = i; } for (p = buf; p != buf + buflen; p = nl + 1, line_no++) { tchar *line_begin, *line_end; size_t line_len; int ret; nl = tmemchr(p, T('\n'), buf + buflen - p); if (!nl) break; line_begin = p; line_end = nl; /* Ignore leading whitespace. */ while (line_begin < nl && istspace(*line_begin)) line_begin++; /* Ignore trailing whitespace. */ while (line_end > line_begin && istspace(*(line_end - 1))) line_end--; line_len = line_end - line_begin; /* Ignore comments and empty lines. */ if (line_len == 0 || *line_begin == T(';') || *line_begin == T('#')) continue; line_begin[line_len] = T('\0'); /* Check for beginning of new section. */ if (line_begin[0] == T('[') && line_begin[line_len - 1] == T(']') && have_named_sections) { line_begin[line_len - 1] = T('\0'); current_section = IN_UNKNOWN_SECTION; for (int i = 0; i < num_pos_sections; i++) { if (!tstrcmp(line_begin + 1, pos_sections[i].name)) { current_section = i; break; } } line_begin[line_len - 1] = T(']'); if (current_section < 0) { if (!(flags & LOAD_TEXT_FILE_NO_WARNINGS)) { WARNING("%"TS":%lu: Unrecognized section \"%"TS"\"", path, line_no, line_begin); } } continue; } if (current_section < 0) { if (current_section == NOT_IN_SECTION) { if (!(flags & LOAD_TEXT_FILE_NO_WARNINGS)) { WARNING("%"TS":%lu: Not in a bracketed section!", path, line_no); } } continue; } if (flags & LOAD_TEXT_FILE_REMOVE_QUOTES) { if (line_begin[0] == T('"') || line_begin[0] == T('\'')) { tchar quote = line_begin[0]; if (line_len >= 2 && line_begin[line_len - 1] == quote) { line_begin++; line_len -= 2; line_begin[line_len] = T('\0'); } } } if (mangle_line) { ret = (*mangle_line)(line_begin, path, line_no); if (ret) return ret; } ret = string_list_append(pos_sections[current_section].strings, line_begin); if (ret) return ret; } return 0; } /** * load_text_file - * * Read and parse lines from a text file given as an on-disk file, standard * input, or a buffer. The file may contain sections, like in an INI file. * * @path * If @buf is NULL, then either the path to the file on-disk to read, or * NULL to read from standard input. Else, a dummy name for the buffer. * @buf * If NULL, the data will be read from the @path file. Otherwise the data * will be read from this buffer. * @bufsize * Length of buffer in bytes; ignored if @buf is NULL. * @mem_ret * On success, a pointer to a buffer backing the parsed lines is stored * here. This must be freed after the parsed lines are done being used. * @pos_sections * Specifications of allowed sections in the file. Each such specification * consists of the name of the section (e.g. [ExclusionList], like in the * INI file format), along with a pointer to the list of lines parsed for * that section. Use an empty name to indicate the destination of lines * not in any section. Each list must be initialized to empty. * @num_pos_sections * Number of entries in the @pos_sections array. * @flags * Flags: LOAD_TEXT_FILE_* flags. * @mangle_line * Optional callback to validate and/or modify each line being read. * * Returns 0 on success; nonzero on failure. * * Unknown sections are ignored, but a warning is printed for each, unless * LOAD_TEXT_FILE_NO_WARNINGS is specified. */ int load_text_file(const tchar *path, const void *buf, size_t bufsize, void **mem_ret, const struct text_file_section *pos_sections, int num_pos_sections, int flags, line_mangle_t mangle_line) { int ret; bool is_filemode = (buf == NULL); bool is_stdin = (is_filemode && path == NULL); tchar *tstr; size_t tstr_nchars; if (is_stdin && !(flags & LOAD_TEXT_FILE_ALLOW_STDIN)) return WIMLIB_ERR_INVALID_PARAM; if (is_filemode) { if (is_stdin) ret = stdin_get_contents((void **)&buf, &bufsize); else ret = read_file_contents(path, (void **)&buf, &bufsize); if (ret) return ret; } ret = translate_text_buffer(buf, bufsize, &tstr, &tstr_nchars); if (is_filemode) FREE((void *)buf); if (ret) return ret; tstr[tstr_nchars++] = T('\n'); ret = parse_text_file(is_stdin ? T("") : path, tstr, tstr_nchars, pos_sections, num_pos_sections, flags, mangle_line); if (ret) { for (int i = 0; i < num_pos_sections; i++) FREE(pos_sections[i].strings->strings); FREE(tstr); return ret; } *mem_ret = tstr; return 0; } wimlib-1.13.1/src/mount_image.c0000644000175000017500000020140613160354225013244 00000000000000/* * mount_image.c * * This file implements mounting of WIM images using FUSE * (Filesystem in Userspace). See http://fuse.sourceforge.net/. * * Currently it is only expected to work on Linux. */ /* * Copyright (C) 2012-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib.h" #include "wimlib/error.h" #ifdef WITH_FUSE #ifdef __WIN32__ # error "FUSE mount not supported on Windows! Please configure --without-fuse" #endif #define FUSE_USE_VERSION 26 #include /* sometimes required before */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wimlib/blob_table.h" #include "wimlib/dentry.h" #include "wimlib/encoding.h" #include "wimlib/metadata.h" #include "wimlib/paths.h" #include "wimlib/progress.h" #include "wimlib/reparse.h" #include "wimlib/timestamp.h" #include "wimlib/unix_data.h" #include "wimlib/write.h" #include "wimlib/xml.h" #ifndef O_NOFOLLOW # define O_NOFOLLOW 0 /* Security only... */ #endif #ifndef ENOATTR # define ENOATTR ENODATA #endif #define WIMFS_MQUEUE_NAME_LEN 32 #define WIMLIB_UNMOUNT_FLAG_SEND_PROGRESS 0x80000000 struct wimfs_unmount_info { unsigned unmount_flags; char mq_name[WIMFS_MQUEUE_NAME_LEN + 1]; }; struct commit_progress_report { enum wimlib_progress_msg msg; union wimlib_progress_info info; }; /* Description of an open file on a mounted WIM image. Actually, this * represents the open state of a particular data stream of an inode, rather * than the inode itself. (An inode might have multiple named data streams in * addition to the default, unnamed data stream.) At a given time, an inode in * the WIM image might have multiple file descriptors open to it, each to any * one of its data streams. */ struct wimfs_fd { /* Pointer to the inode of this open file. * 'i_num_opened_fds' of the inode tracks the number of file descriptors * that reference it. */ struct wim_inode *f_inode; /* Pointer to the blob descriptor for the data stream that has been * opened. 'num_opened_fds' of the blob descriptor tracks the number of * file descriptors that reference it. Or, this value may be NULL, * which indicates that the opened stream is empty and consequently does * not have a blob descriptor. */ struct blob_descriptor *f_blob; /* If valid (filedes_valid(&f_staging_fd)), this contains the * corresponding native file descriptor for the staging file that has * been created for reading from and/or writing to this open stream. A * single staging file might have multiple file descriptors open to it * simultaneously, each used by a different 'struct wimfs_fd'. * * Or, if invalid (!filedes_valid(&f_staging_fd)), this 'struct * wimfs_fd' is not associated with a staging file. This is permissible * only if this 'struct wimfs_fd' was opened read-only and the stream * has not yet been extracted to a staging file. */ struct filedes f_staging_fd; /* 0-based index of this file descriptor in the file descriptor table of * its inode. */ u16 f_idx; /* Unique ID of the opened stream in the inode. This will stay the same * even if the indices of the inode's streams are changed by a deletion. */ u32 f_stream_id; }; #define WIMFS_FD(fi) ((struct wimfs_fd *)(uintptr_t)((fi)->fh)) /* Context structure for a mounted WIM image. */ struct wimfs_context { /* The WIMStruct containing the mounted image. The mounted image is the * currently selected image (wim->current_image). */ WIMStruct *wim; /* Flags passed to wimlib_mount_image() (WIMLIB_MOUNT_FLAG_*). */ int mount_flags; /* Default flags for path lookup in the WIM image. */ int default_lookup_flags; /* Information about the user who has mounted the WIM image */ uid_t owner_uid; gid_t owner_gid; /* Absolute path to the mountpoint directory (may be needed for absolute * symbolic link fixups) */ char *mountpoint_abspath; size_t mountpoint_abspath_nchars; /* Information about the staging directory for a read-write mount. */ int parent_dir_fd; int staging_dir_fd; char *staging_dir_name; /* For read-write mounts, the inode number to be assigned to the next * created file. Note: since this isn't a persistent filesystem and we * can re-assign the inode numbers just before mounting the image, it's * good enough to just generate inode numbers sequentially. */ u64 next_ino; /* Number of file descriptors open to the mounted WIM image. */ unsigned long num_open_fds; /* For read-write mounts, the original metadata resource of the mounted * image. */ struct blob_descriptor *metadata_resource; /* Parameters for unmounting the image (can be set via extended * attribute "wimfs.unmount_info"). */ struct wimfs_unmount_info unmount_info; }; #define WIMFS_CTX(fuse_ctx) ((struct wimfs_context*)(fuse_ctx)->private_data) /* Retrieve the context structure for the currently mounted WIM image. * * Note: this is a per-thread variable. It is possible for different threads to * mount different images at the same time in the same process, although they * must use different WIMStructs! */ static inline struct wimfs_context * wimfs_get_context(void) { return WIMFS_CTX(fuse_get_context()); } static void wimfs_inc_num_open_fds(void) { wimfs_get_context()->num_open_fds++; } static void wimfs_dec_num_open_fds(void) { wimfs_get_context()->num_open_fds--; } /* Retrieve the WIMStruct for the currently mounted WIM image. */ static inline WIMStruct * wimfs_get_WIMStruct(void) { return wimfs_get_context()->wim; } /* Is write permission requested on the file? */ static inline bool flags_writable(int open_flags) { int accmode = (open_flags & O_ACCMODE); return (accmode == O_RDWR || accmode == O_WRONLY); } static mode_t fuse_mask_mode(mode_t mode, const struct fuse_context *fuse_ctx) { #if FUSE_MAJOR_VERSION > 2 || (FUSE_MAJOR_VERSION == 2 && FUSE_MINOR_VERSION >= 8) mode &= ~fuse_ctx->umask; #endif return mode; } /* * Allocate a file descriptor to a data stream in the mounted WIM image. * * @inode * The inode containing the stream being opened * @strm * The stream of the inode being opened * @fd_ret * On success, a pointer to the new file descriptor will be stored here. * * Returns 0 or a -errno code. */ static int alloc_wimfs_fd(struct wim_inode *inode, struct wim_inode_stream *strm, struct wimfs_fd **fd_ret) { static const u16 min_fds_per_alloc = 8; static const u16 max_fds = 0xffff; u16 i; struct wimfs_fd *fd; if (inode->i_num_opened_fds == inode->i_num_allocated_fds) { u16 num_new_fds; struct wimfs_fd **fds; /* Expand this inode's file descriptor table. */ num_new_fds = max(min_fds_per_alloc, inode->i_num_allocated_fds / 4); num_new_fds = min(num_new_fds, max_fds - inode->i_num_allocated_fds); if (num_new_fds == 0) return -EMFILE; fds = REALLOC(inode->i_fds, (inode->i_num_allocated_fds + num_new_fds) * sizeof(fds[0])); if (!fds) return -ENOMEM; memset(&fds[inode->i_num_allocated_fds], 0, num_new_fds * sizeof(fds[0])); inode->i_fds = fds; inode->i_num_allocated_fds += num_new_fds; inode->i_next_fd = inode->i_num_opened_fds; } /* Allocate the file descriptor in the first available space in the * inode's file descriptor table. * * i_next_fd is the lower bound on the next open slot. */ for (i = inode->i_next_fd; inode->i_fds[i]; i++) ; fd = MALLOC(sizeof(*fd)); if (!fd) return -ENOMEM; fd->f_inode = inode; fd->f_blob = stream_blob_resolved(strm); filedes_invalidate(&fd->f_staging_fd); fd->f_idx = i; fd->f_stream_id = strm->stream_id; *fd_ret = fd; inode->i_fds[i] = fd; inode->i_num_opened_fds++; if (fd->f_blob) fd->f_blob->num_opened_fds++; wimfs_inc_num_open_fds(); inode->i_next_fd = i + 1; return 0; } /* * Close a file descriptor to a data stream in the mounted WIM image. * * Returns 0 or a -errno code. The file descriptor is always closed. */ static int close_wimfs_fd(struct wimfs_fd *fd) { int ret = 0; struct wim_inode *inode; /* Close the staging file if open. */ if (filedes_valid(&fd->f_staging_fd)) if (filedes_close(&fd->f_staging_fd)) ret = -errno; /* Release this file descriptor from its blob descriptor. */ if (fd->f_blob) blob_decrement_num_opened_fds(fd->f_blob); wimfs_dec_num_open_fds(); /* Release this file descriptor from its inode. */ inode = fd->f_inode; inode->i_fds[fd->f_idx] = NULL; if (fd->f_idx < inode->i_next_fd) inode->i_next_fd = fd->f_idx; FREE(fd); inode_dec_num_opened_fds(inode); return ret; } /* * Translate a path into the corresponding inode in the mounted WIM image. * * See get_dentry() for more information. * * Returns a pointer to the resulting inode, or NULL with errno set. */ static struct wim_inode * wim_pathname_to_inode(WIMStruct *wim, const char *path) { struct wim_dentry *dentry; dentry = get_dentry(wim, path, WIMLIB_CASE_SENSITIVE); if (!dentry) return NULL; return dentry->d_inode; } /* Can look up named data stream with colon syntax */ #define LOOKUP_FLAG_ADS_OK 0x01 /* Can look up directory (otherwise get -ENOTDIR) */ #define LOOKUP_FLAG_DIRECTORY_OK 0x02 /* Get the data stream of the specified name from the specified inode. Returns * NULL with errno set if not found. */ static struct wim_inode_stream * inode_get_data_stream_tstr(const struct wim_inode *inode, const char *stream_name) { struct wim_inode_stream *strm; if (!stream_name || !*stream_name) { strm = inode_get_unnamed_data_stream(inode); } else { const utf16lechar *uname; if (tstr_get_utf16le(stream_name, &uname)) return NULL; strm = inode_get_stream(inode, STREAM_TYPE_DATA, uname); tstr_put_utf16le(uname); } if (!strm) errno = ENOENT; return strm; } /* * Translate a path into the corresponding dentry and stream in the mounted WIM * image. * * Returns 0 or a -errno code. @dentry_ret and @strm_ret are both optional. */ static int wim_pathname_to_stream(const struct wimfs_context *ctx, const char *path, int lookup_flags, struct wim_dentry **dentry_ret, struct wim_inode_stream **strm_ret) { WIMStruct *wim = ctx->wim; struct wim_dentry *dentry; struct wim_inode *inode; struct wim_inode_stream *strm; const char *stream_name = NULL; char *p = NULL; lookup_flags |= ctx->default_lookup_flags; if (lookup_flags & LOOKUP_FLAG_ADS_OK) { stream_name = path_stream_name(path); if (stream_name) { p = (char *)stream_name - 1; *p = '\0'; } } dentry = get_dentry(wim, path, WIMLIB_CASE_SENSITIVE); if (p) *p = ':'; if (!dentry) return -errno; inode = dentry->d_inode; if (inode_resolve_streams(inode, wim->blob_table, false)) return -EIO; if (!(lookup_flags & LOOKUP_FLAG_DIRECTORY_OK) && inode_is_directory(inode)) return -EISDIR; strm = inode_get_data_stream_tstr(inode, stream_name); if (!strm) { /* Force creation of an unnamed data stream */ if (!stream_name) strm = inode_add_stream(inode, STREAM_TYPE_DATA, NO_STREAM_NAME, NULL); if (!strm) return -errno; } if (dentry_ret) *dentry_ret = dentry; if (strm_ret) *strm_ret = strm; return 0; } /* * Create a new file in the mounted WIM image. * * @fuse_ctx * The FUSE context for the mounted image. * @path * The path at which to create the first link to the new file. If a file * already exists at this path, -EEXIST is returned. * @mode * The UNIX mode for the new file. This is only fully honored if * WIMLIB_MOUNT_FLAG_UNIX_DATA was passed to wimlib_mount_image(). * @rdev * The device ID for the new file, encoding the major and minor device * numbers. This is only honored if WIMLIB_MOUNT_FLAG_UNIX_DATA was passed * to wimlib_mount_image(). * @dentry_ret * On success, a pointer to the new dentry is returned here. Its d_inode * member will point to the new inode that was created for it and added to * the mounted WIM image. * * Returns 0 or a -errno code. */ static int create_file(struct fuse_context *fuse_ctx, const char *path, mode_t mode, dev_t rdev, struct wim_dentry **dentry_ret) { struct wimfs_context *wimfs_ctx = WIMFS_CTX(fuse_ctx); struct wim_dentry *parent; const char *basename; struct wim_dentry *dentry; struct wim_inode *inode; parent = get_parent_dentry(wimfs_ctx->wim, path, WIMLIB_CASE_SENSITIVE); if (!parent) return -errno; if (!dentry_is_directory(parent)) return -ENOTDIR; basename = path_basename(path); if (get_dentry_child_with_name(parent, basename, WIMLIB_CASE_SENSITIVE)) return -EEXIST; if (new_dentry_with_new_inode(basename, true, &dentry)) return -ENOMEM; inode = dentry->d_inode; inode->i_ino = wimfs_ctx->next_ino++; /* Note: we still use FILE_ATTRIBUTE_NORMAL for device nodes, named * pipes, and sockets. The real mode is in the UNIX metadata. */ if (S_ISDIR(mode)) inode->i_attributes = FILE_ATTRIBUTE_DIRECTORY; else inode->i_attributes = FILE_ATTRIBUTE_NORMAL; if (wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA) { struct wimlib_unix_data unix_data; unix_data.uid = fuse_ctx->uid; unix_data.gid = fuse_ctx->gid; unix_data.mode = fuse_mask_mode(mode, fuse_ctx); unix_data.rdev = rdev; if (!inode_set_unix_data(inode, &unix_data, UNIX_DATA_ALL)) { free_dentry(dentry); return -ENOMEM; } } hlist_add_head(&inode->i_hlist_node, &wim_get_current_image_metadata(wimfs_ctx->wim)->inode_list); dentry_add_child(parent, dentry); *dentry_ret = dentry; return 0; } /* * Remove a dentry from the mounted WIM image; i.e. remove an alias for an * inode. */ static void remove_dentry(struct wim_dentry *dentry, struct blob_table *blob_table) { /* Drop blob references. */ inode_unref_blobs(dentry->d_inode, blob_table); /* Unlink the dentry from the image's dentry tree. */ unlink_dentry(dentry); /* Delete the dentry. This will also decrement the link count of the * corresponding inode, and possibly cause it to be deleted as well. */ free_dentry(dentry); } /* Generate UNIX filetype mode bits for the specified WIM inode, based on its * Windows file attributes. */ static mode_t inode_unix_file_type(const struct wim_inode *inode) { if (inode_is_symlink(inode)) return S_IFLNK; else if (inode_is_directory(inode)) return S_IFDIR; else return S_IFREG; } /* Generate a default UNIX mode for the specified WIM inode. */ static mode_t inode_default_unix_mode(const struct wim_inode *inode) { return inode_unix_file_type(inode) | 0777; } static u64 blob_size(const struct blob_descriptor *blob) { if (!blob) return 0; return blob->size; } static u64 blob_stored_size(const struct blob_descriptor *blob) { if (!blob) return 0; if (blob->blob_location == BLOB_IN_WIM && blob->size == blob->rdesc->uncompressed_size) return blob->rdesc->size_in_wim; return blob->size; } /* * Retrieve standard UNIX metadata ('struct stat') for a WIM inode. * * @blob is the blob descriptor for the stream of the inode that is being * queried, or NULL. We mostly return the same information for all streams, but * st_size and st_blocks may be different for different streams. * * This always returns 0. */ static int inode_to_stbuf(const struct wim_inode *inode, const struct blob_descriptor *blob, struct stat *stbuf) { const struct wimfs_context *ctx = wimfs_get_context(); struct wimlib_unix_data unix_data; memset(stbuf, 0, sizeof(struct stat)); if ((ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA) && inode_get_unix_data(inode, &unix_data)) { /* Use the user ID, group ID, mode, and device ID from the * inode's extra UNIX metadata information. */ stbuf->st_uid = unix_data.uid; stbuf->st_gid = unix_data.gid; stbuf->st_mode = unix_data.mode; stbuf->st_rdev = unix_data.rdev; } else { /* Generate default values for the user ID, group ID, and mode. * * Note: in the case of an allow_other mount, fuse_context.uid * may not be the same as wimfs_context.owner_uid! */ stbuf->st_uid = ctx->owner_uid; stbuf->st_gid = ctx->owner_gid; stbuf->st_mode = inode_default_unix_mode(inode); } stbuf->st_ino = inode->i_ino; stbuf->st_nlink = inode->i_nlink; stbuf->st_size = blob_size(blob); #ifdef HAVE_STAT_NANOSECOND_PRECISION stbuf->st_atim = wim_timestamp_to_timespec(inode->i_last_access_time); stbuf->st_mtim = wim_timestamp_to_timespec(inode->i_last_write_time); stbuf->st_ctim = stbuf->st_mtim; #else stbuf->st_atime = wim_timestamp_to_time_t(inode->i_last_access_time); stbuf->st_mtime = wim_timestamp_to_time_t(inode->i_last_write_time); stbuf->st_ctime = stbuf->st_mtime; #endif stbuf->st_blocks = DIV_ROUND_UP(blob_stored_size(blob), 512); return 0; } /* Update the last access and last write timestamps of a WIM inode. */ static void touch_inode(struct wim_inode *inode) { u64 now = now_as_wim_timestamp(); inode->i_last_access_time = now; inode->i_last_write_time = now; } static void touch_parent(struct wim_dentry *dentry) { touch_inode(dentry->d_parent->d_inode); } /* * Create a new file in the staging directory for a read-write mounted image. * * On success, returns the file descriptor for the new staging file, opened for * writing. In addition, stores the allocated name of the staging file in * @name_ret. * * On failure, returns -1 and sets errno. */ static int create_staging_file(const struct wimfs_context *ctx, char **name_ret) { static const size_t STAGING_FILE_NAME_LEN = 20; char *name; int fd; name = MALLOC(STAGING_FILE_NAME_LEN + 1); if (!name) return -1; name[STAGING_FILE_NAME_LEN] = '\0'; retry: get_random_alnum_chars(name, STAGING_FILE_NAME_LEN); fd = openat(ctx->staging_dir_fd, name, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0600); if (unlikely(fd < 0)) { if (unlikely(errno == EEXIST)) /* Try again with another name. */ goto retry; FREE(name); } else { *name_ret = name; } return fd; } /* * Extract a blob to the staging directory. This is necessary when a stream * using the blob is being opened for writing and the blob has not already been * extracted to the staging directory. * * @inode * The inode containing the stream being opened for writing. * @strm * The stream being opened for writing. The blob descriptor to which the * stream refers will be changed by this function. * @size * Number of bytes of the blob to extract and include in the staging file. * It may be less than the actual blob length, in which case only a prefix * of the blob will be extracted. It may also be more than the actual blob * length, in which case the extra space will be zero-filled. * * Returns 0 or a -errno code. */ static int extract_blob_to_staging_dir(struct wim_inode *inode, struct wim_inode_stream *strm, off_t size, const struct wimfs_context *ctx) { struct blob_descriptor *old_blob; struct blob_descriptor *new_blob; char *staging_file_name; int staging_fd; off_t extract_size; int result; int ret; old_blob = stream_blob_resolved(strm); /* Create the staging file. */ staging_fd = create_staging_file(ctx, &staging_file_name); if (unlikely(staging_fd < 0)) return -errno; /* Extract the stream to the staging file (possibly truncated). */ if (old_blob) { struct filedes fd; filedes_init(&fd, staging_fd); errno = 0; extract_size = min(old_blob->size, size); result = extract_blob_prefix_to_fd(old_blob, extract_size, &fd); } else { extract_size = 0; result = 0; } /* In the case of truncate() to more than the file length, extend the * staging file with zeroes by calling ftruncate(). */ if (!result && size > extract_size) result = ftruncate(staging_fd, size); /* Close the staging file. */ if (close(staging_fd)) result = -1; /* If an error occurred, unlink the staging file. */ if (unlikely(result)) { /* extract_blob_to_fd() should set errno, but if it didn't, * set a default value. */ ret = errno ? -errno : -EIO; goto out_delete_staging_file; } /* Create a blob descriptor for the staging file. */ new_blob = new_blob_descriptor(); if (unlikely(!new_blob)) { ret = -ENOMEM; goto out_delete_staging_file; } /* There may already be open file descriptors to this stream if it's * previously been opened read-only, but just now we're opening it * read-write. Identify those file descriptors, update them to use the * new blob descriptor, and open staging file descriptors for them. */ for (u16 i = 0, j = 0; j < inode->i_num_opened_fds; i++) { struct wimfs_fd *fd; int raw_fd; fd = inode->i_fds[i]; if (!fd) continue; j++; if (fd->f_stream_id != strm->stream_id) continue; /* This is a readonly fd for the same stream. */ fd->f_blob = new_blob; new_blob->num_opened_fds++; raw_fd = openat(ctx->staging_dir_fd, staging_file_name, O_RDONLY | O_NOFOLLOW); if (unlikely(raw_fd < 0)) { ret = -errno; goto out_revert_fd_changes; } filedes_init(&fd->f_staging_fd, raw_fd); } if (old_blob) old_blob->num_opened_fds -= new_blob->num_opened_fds; new_blob->blob_location = BLOB_IN_STAGING_FILE; new_blob->staging_file_name = staging_file_name; new_blob->staging_dir_fd = ctx->staging_dir_fd; new_blob->size = size; prepare_unhashed_blob(new_blob, inode, strm->stream_id, &wim_get_current_image_metadata(ctx->wim)->unhashed_blobs); inode_replace_stream_blob(inode, strm, new_blob, ctx->wim->blob_table); return 0; out_revert_fd_changes: for (u16 i = 0; new_blob->num_opened_fds; i++) { struct wimfs_fd *fd = inode->i_fds[i]; if (fd && fd->f_stream_id == strm->stream_id) { fd->f_blob = old_blob; if (filedes_valid(&fd->f_staging_fd)) { filedes_close(&fd->f_staging_fd); filedes_invalidate(&fd->f_staging_fd); } new_blob->num_opened_fds--; } } free_blob_descriptor(new_blob); out_delete_staging_file: unlinkat(ctx->staging_dir_fd, staging_file_name, 0); FREE(staging_file_name); return ret; } /* * Create the staging directory for the WIM file. * * The staging directory will be created in the directory specified by the open * file descriptor @parent_dir_fd. It will be given a randomly generated name * based on @wim_basename, the name of the WIM file. * * On success, returns a file descriptor to the open staging directory with * O_RDONLY access. In addition, stores the allocated name of the staging * directory (relative to @parent_dir_fd) in @staging_dir_name_ret. * On failure, returns -1 and sets errno. */ static int make_staging_dir_at(int parent_dir_fd, const char *wim_basename, char **staging_dir_name_ret) { static const char common_suffix[8] = ".staging"; static const size_t random_suffix_len = 10; size_t wim_basename_len; size_t staging_dir_name_len; char *staging_dir_name; char *p; int fd; wim_basename_len = strlen(wim_basename); staging_dir_name_len = wim_basename_len + sizeof(common_suffix) + random_suffix_len; staging_dir_name = MALLOC(staging_dir_name_len + 1); if (!staging_dir_name) return -1; p = staging_dir_name; p = mempcpy(p, wim_basename, wim_basename_len); p = mempcpy(p, common_suffix, sizeof(common_suffix)); get_random_alnum_chars(p, random_suffix_len); p += random_suffix_len; *p = '\0'; if (mkdirat(parent_dir_fd, staging_dir_name, 0700)) goto err1; fd = openat(parent_dir_fd, staging_dir_name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW); if (fd < 0) goto err2; *staging_dir_name_ret = staging_dir_name; return fd; err2: unlinkat(parent_dir_fd, staging_dir_name, AT_REMOVEDIR); err1: FREE(staging_dir_name); return -1; } /* * Create the staging directory and set ctx->staging_dir_fd, * ctx->staging_dir_name, and ctx->parent_dir_fd. */ static int make_staging_dir(struct wimfs_context *ctx, const char *parent_dir_path) { const char *wim_basename; char *end = NULL; int ret; wim_basename = path_basename(ctx->wim->filename); if (!parent_dir_path) { /* The user did not specify a directory. Default to creating * the staging directory alongside the WIM file. */ if (wim_basename > ctx->wim->filename) { parent_dir_path = ctx->wim->filename; end = (char *)(wim_basename - 1); /* *end must be a slash. Temporarily overwrite it so we * can open the parent directory. */ *end = '\0'; } else { parent_dir_path = "."; } } /* Open the parent directory (in which we'll create our staging * directory). */ ctx->parent_dir_fd = open(parent_dir_path, O_RDONLY | O_DIRECTORY); if (ctx->parent_dir_fd < 0) { ERROR_WITH_ERRNO("Can't open directory \"%s\"", parent_dir_path); ret = WIMLIB_ERR_OPENDIR; goto out_restore_wim_filename; } ctx->staging_dir_fd = make_staging_dir_at(ctx->parent_dir_fd, wim_basename, &ctx->staging_dir_name); if (ctx->staging_dir_fd < 0) { ERROR_WITH_ERRNO("Can't create staging directory in \"%s\"", parent_dir_path); close(ctx->parent_dir_fd); ret = WIMLIB_ERR_MKDIR; goto out_restore_wim_filename; } ret = 0; out_restore_wim_filename: if (end) *end = '/'; return ret; } /* Deletes the staging directory, undoing the effects of a successful call to * make_staging_dir(). */ static void delete_staging_dir(struct wimfs_context *ctx) { DIR *dir; struct dirent *ent; dir = fdopendir(ctx->staging_dir_fd); if (dir) { while ((ent = readdir(dir))) unlinkat(ctx->staging_dir_fd, ent->d_name, 0); closedir(dir); } else { close(ctx->staging_dir_fd); } if (unlinkat(ctx->parent_dir_fd, ctx->staging_dir_name, AT_REMOVEDIR)) WARNING_WITH_ERRNO("Could not delete staging directory"); FREE(ctx->staging_dir_name); close(ctx->parent_dir_fd); } static void prepare_inodes(struct wimfs_context *ctx) { struct wim_image_metadata *imd; struct wim_inode *inode; ctx->next_ino = 1; imd = wim_get_current_image_metadata(ctx->wim); image_for_each_inode(inode, imd) { inode->i_ino = ctx->next_ino++; inode->i_num_opened_fds = 0; inode->i_num_allocated_fds = 0; inode->i_fds = NULL; } } /* Delete the 'struct blob_descriptor' for any stream that was modified * or created in the read-write mounted image and had a final size of 0. */ static void delete_empty_blobs(struct wimfs_context *ctx) { struct blob_descriptor *blob, *tmp; struct wim_image_metadata *imd; imd = wim_get_current_image_metadata(ctx->wim); image_for_each_unhashed_blob_safe(blob, tmp, imd) { if (!blob->size) { *retrieve_pointer_to_unhashed_blob(blob) = NULL; list_del(&blob->unhashed_list); free_blob_descriptor(blob); } } } /* Close all file descriptors open to the specified inode. * * Note: closing the last file descriptor might free the inode. */ static void inode_close_fds(struct wim_inode *inode) { u16 num_open_fds = inode->i_num_opened_fds; for (u16 i = 0; num_open_fds; i++) { if (inode->i_fds[i]) { close_wimfs_fd(inode->i_fds[i]); num_open_fds--; } } } /* Close all file descriptors open to the mounted image. */ static void close_all_fds(struct wimfs_context *ctx) { struct wim_inode *inode; struct hlist_node *tmp; struct wim_image_metadata *imd; imd = wim_get_current_image_metadata(ctx->wim); image_for_each_inode_safe(inode, tmp, imd) inode_close_fds(inode); } /* Moves the currently selected image, which may have been modified, to a new * index, and sets the original index to refer to a reset (unmodified) copy of * the image. */ static int renew_current_image(struct wimfs_context *ctx) { WIMStruct *wim = ctx->wim; int image = wim->current_image; struct wim_image_metadata *imd; struct wim_inode *inode; int ret; ret = WIMLIB_ERR_NOMEM; imd = new_unloaded_image_metadata(ctx->metadata_resource); if (!imd) goto err; ret = append_image_metadata(wim, wim->image_metadata[image - 1]); if (ret) goto err_put_imd; ret = xml_export_image(wim->xml_info, image, wim->xml_info, NULL, NULL, false); if (ret) goto err_undo_append; wim->image_metadata[image - 1] = imd; wim->current_image = wim->hdr.image_count; ret = select_wim_image(wim, image); if (ret) goto err_undo_export; image_for_each_inode(inode, imd) { for (unsigned i = 0; i < inode->i_num_streams; i++) { struct blob_descriptor *blob; blob = stream_blob(&inode->i_streams[i], wim->blob_table); if (blob) blob->refcnt += inode->i_nlink; } } select_wim_image(wim, wim->hdr.image_count); ctx->metadata_resource = NULL; return 0; err_undo_export: xml_delete_image(wim->xml_info, wim->hdr.image_count); wim->image_metadata[image - 1] = wim->image_metadata[wim->hdr.image_count - 1]; wim->current_image = image; err_undo_append: wim->hdr.image_count--; err_put_imd: imd->metadata_blob = NULL; put_image_metadata(imd); err: return ret; } static enum wimlib_progress_status commit_progress_func(enum wimlib_progress_msg msg, union wimlib_progress_info *info, void *progctx) { mqd_t mq = *(mqd_t *)progctx; struct commit_progress_report report; memset(&report, 0, sizeof(report)); report.msg = msg; if (info) report.info = *info; mq_send(mq, (const char *)&report, sizeof(report), 1); return WIMLIB_PROGRESS_STATUS_CONTINUE; } /* Commit the mounted image to the underlying WIM file. */ static int commit_image(struct wimfs_context *ctx, int unmount_flags, mqd_t mq) { int write_flags; if (unmount_flags & WIMLIB_UNMOUNT_FLAG_SEND_PROGRESS) wimlib_register_progress_function(ctx->wim, commit_progress_func, &mq); else wimlib_register_progress_function(ctx->wim, NULL, NULL); if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) { int ret = renew_current_image(ctx); if (ret) return ret; } delete_empty_blobs(ctx); write_flags = 0; if (unmount_flags & WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY) write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; if (unmount_flags & WIMLIB_UNMOUNT_FLAG_REBUILD) write_flags |= WIMLIB_WRITE_FLAG_REBUILD; if (unmount_flags & WIMLIB_UNMOUNT_FLAG_RECOMPRESS) write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS; return wimlib_overwrite(ctx->wim, write_flags, 0); } /* In the case of an allow_other mount, only the mount owner and root are * allowed to unmount the filesystem. */ static bool may_unmount_wimfs(void) { const struct fuse_context *fuse_ctx = fuse_get_context(); const struct wimfs_context *wimfs_ctx = WIMFS_CTX(fuse_ctx); return (fuse_ctx->uid == wimfs_ctx->owner_uid || fuse_ctx->uid == 0); } /* Unmount the mounted image, called from the daemon process. */ static int unmount_wimfs(void) { struct fuse_context *fuse_ctx = fuse_get_context(); struct wimfs_context *wimfs_ctx = WIMFS_CTX(fuse_ctx); const struct wimfs_unmount_info *info = &wimfs_ctx->unmount_info; int unmount_flags = info->unmount_flags; mqd_t mq = (mqd_t)-1; int ret; /* Ignore COMMIT if the image is mounted read-only. */ if (!(wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)) unmount_flags &= ~WIMLIB_UNMOUNT_FLAG_COMMIT; if (unmount_flags & WIMLIB_UNMOUNT_FLAG_SEND_PROGRESS) { mq = mq_open(info->mq_name, O_WRONLY | O_NONBLOCK); if (mq == (mqd_t)-1) { ret = WIMLIB_ERR_MQUEUE; goto out; } } if (wimfs_ctx->num_open_fds) { /* There are still open file descriptors to the image. */ /* With COMMIT, refuse to unmount unless FORCE is also * specified. */ if ((unmount_flags & (WIMLIB_UNMOUNT_FLAG_COMMIT | WIMLIB_UNMOUNT_FLAG_FORCE)) == WIMLIB_UNMOUNT_FLAG_COMMIT) { ret = WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY; goto out; } /* Force-close all file descriptors. */ close_all_fds(wimfs_ctx); } if (unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) ret = commit_image(wimfs_ctx, unmount_flags, mq); else ret = 0; /* Read-only mount, or discarding changes to a read-write mount */ out: /* Leave the image mounted if commit failed, unless this is a * forced unmount. The user can retry without COMMIT if they * want. */ if (!ret || (unmount_flags & WIMLIB_UNMOUNT_FLAG_FORCE)) { unlock_wim_for_append(wimfs_ctx->wim); fuse_exit(fuse_ctx->fuse); } if (mq != (mqd_t)-1) mq_close(mq); return ret; } static int wimfs_chmod(const char *path, mode_t mask) { const struct wimfs_context *ctx = wimfs_get_context(); struct wim_inode *inode; struct wimlib_unix_data unix_data; if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA)) return -EOPNOTSUPP; inode = wim_pathname_to_inode(ctx->wim, path); if (!inode) return -errno; unix_data.uid = ctx->owner_uid; unix_data.gid = ctx->owner_gid; unix_data.mode = mask; unix_data.rdev = 0; if (!inode_set_unix_data(inode, &unix_data, UNIX_DATA_MODE)) return -ENOMEM; return 0; } static int wimfs_chown(const char *path, uid_t uid, gid_t gid) { const struct wimfs_context *ctx = wimfs_get_context(); struct wim_inode *inode; struct wimlib_unix_data unix_data; int which; if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA)) return -EOPNOTSUPP; inode = wim_pathname_to_inode(ctx->wim, path); if (!inode) return -errno; which = 0; if (uid != (uid_t)-1) which |= UNIX_DATA_UID; else uid = ctx->owner_uid; if (gid != (gid_t)-1) which |= UNIX_DATA_GID; else gid = ctx->owner_gid; unix_data.uid = uid; unix_data.gid = gid; unix_data.mode = inode_default_unix_mode(inode); unix_data.rdev = 0; if (!inode_set_unix_data(inode, &unix_data, which)) return -ENOMEM; return 0; } static int wimfs_fgetattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) { struct wimfs_fd *fd = WIMFS_FD(fi); return inode_to_stbuf(fd->f_inode, fd->f_blob, stbuf); } static int wimfs_ftruncate(const char *path, off_t size, struct fuse_file_info *fi) { struct wimfs_fd *fd = WIMFS_FD(fi); if (ftruncate(fd->f_staging_fd.fd, size)) return -errno; touch_inode(fd->f_inode); fd->f_blob->size = size; return 0; } static int wimfs_getattr(const char *path, struct stat *stbuf) { const struct wimfs_context *ctx = wimfs_get_context(); struct wim_dentry *dentry; struct wim_inode_stream *strm; int ret; ret = wim_pathname_to_stream(ctx, path, LOOKUP_FLAG_DIRECTORY_OK, &dentry, &strm); if (ret) return ret; return inode_to_stbuf(dentry->d_inode, stream_blob_resolved(strm), stbuf); } static int copy_xattr(char *dest, size_t destsize, const void *src, size_t srcsize) { if (destsize) { if (destsize < srcsize) return -ERANGE; memcpy(dest, src, srcsize); } return srcsize; } static int wimfs_getxattr(const char *path, const char *name, char *value, size_t size) { const struct wimfs_context *ctx = wimfs_get_context(); const struct wim_inode *inode; const struct wim_inode_stream *strm; const struct blob_descriptor *blob; if (!strncmp(name, "wimfs.", 6)) { /* Handle some magical extended attributes. These really should * be ioctls, but directory ioctls aren't supported until * libfuse 2.9, and even then they are broken. */ name += 6; if (!strcmp(name, "wim_filename")) { return copy_xattr(value, size, ctx->wim->filename, strlen(ctx->wim->filename)); } if (!strcmp(name, "wim_info")) { struct wimlib_wim_info info; wimlib_get_wim_info(ctx->wim, &info); return copy_xattr(value, size, &info, sizeof(info)); } if (!strcmp(name, "mounted_image")) { return copy_xattr(value, size, &ctx->wim->current_image, sizeof(int)); } if (!strcmp(name, "mount_flags")) { return copy_xattr(value, size, &ctx->mount_flags, sizeof(int)); } if (!strcmp(name, "unmount")) { if (!may_unmount_wimfs()) return -EPERM; if (size) { int status; if (size < sizeof(int)) return -ERANGE; status = unmount_wimfs(); memcpy(value, &status, sizeof(int)); } return sizeof(int); } return -ENOATTR; } if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR)) return -ENOTSUP; if (strncmp(name, "user.", 5)) return -ENOATTR; name += 5; if (!*name) return -ENOATTR; /* Querying a named data stream */ inode = wim_pathname_to_inode(ctx->wim, path); if (!inode) return -errno; strm = inode_get_data_stream_tstr(inode, name); if (!strm) return (errno == ENOENT) ? -ENOATTR : -errno; blob = stream_blob_resolved(strm); if (!blob) return 0; if (unlikely(blob->size > INT_MAX)) return -EFBIG; if (size) { if (size < blob->size) return -ERANGE; if (read_blob_into_buf(blob, value)) return errno ? -errno : -EIO; } return blob->size; } static int wimfs_link(const char *existing_path, const char *new_path) { WIMStruct *wim = wimfs_get_WIMStruct(); const char *new_name; struct wim_inode *inode; struct wim_dentry *dir; struct wim_dentry *new_alias; inode = wim_pathname_to_inode(wim, existing_path); if (!inode) return -errno; if (inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)) return -EPERM; new_name = path_basename(new_path); dir = get_parent_dentry(wim, new_path, WIMLIB_CASE_SENSITIVE); if (!dir) return -errno; if (!dentry_is_directory(dir)) return -ENOTDIR; if (get_dentry_child_with_name(dir, new_name, WIMLIB_CASE_SENSITIVE)) return -EEXIST; if (new_dentry_with_existing_inode(new_name, inode, &new_alias)) return -ENOMEM; dentry_add_child(dir, new_alias); touch_inode(dir->d_inode); return 0; } static int wimfs_listxattr(const char *path, char *list, size_t size) { const struct wimfs_context *ctx = wimfs_get_context(); const struct wim_inode *inode; char *p = list; char *end = list + size; int total_size = 0; if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR)) return -ENOTSUP; /* List named data streams, or get the list size. We report each named * data stream "X" as an extended attribute "user.X". */ inode = wim_pathname_to_inode(ctx->wim, path); if (!inode) return -errno; for (unsigned i = 0; i < inode->i_num_streams; i++) { const struct wim_inode_stream *strm; char *stream_name_mbs; size_t stream_name_mbs_nbytes; strm = &inode->i_streams[i]; if (!stream_is_named_data_stream(strm)) continue; if (utf16le_to_tstr(strm->stream_name, utf16le_len_bytes(strm->stream_name), &stream_name_mbs, &stream_name_mbs_nbytes)) return -errno; if (unlikely(INT_MAX - total_size < stream_name_mbs_nbytes + 6)) { FREE(stream_name_mbs); return -EFBIG; } total_size += stream_name_mbs_nbytes + 6; if (size) { if (end - p < stream_name_mbs_nbytes + 6) { FREE(stream_name_mbs); return -ERANGE; } p = mempcpy(p, "user.", 5); p = mempcpy(p, stream_name_mbs, stream_name_mbs_nbytes); *p++ = '\0'; } FREE(stream_name_mbs); } return total_size; } static int wimfs_mkdir(const char *path, mode_t mode) { struct wim_dentry *dentry; int ret; /* Note: according to fuse.h, mode may not include S_IFDIR */ ret = create_file(fuse_get_context(), path, mode | S_IFDIR, 0, &dentry); if (ret) return ret; touch_parent(dentry); return 0; } static int wimfs_mknod(const char *path, mode_t mode, dev_t rdev) { struct fuse_context *fuse_ctx = fuse_get_context(); struct wimfs_context *wimfs_ctx = WIMFS_CTX(fuse_ctx); const char *stream_name; if ((wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS) && (stream_name = path_stream_name(path))) { struct wim_inode *inode; struct wim_inode_stream *existing_strm; struct wim_inode_stream *new_strm; char *p; const utf16lechar *uname; /* Create a named data stream. */ if (!S_ISREG(mode)) return -EOPNOTSUPP; p = (char *)stream_name - 1; *p = '\0'; inode = wim_pathname_to_inode(wimfs_ctx->wim, path); *p = ':'; if (!inode) return -errno; if (tstr_get_utf16le(stream_name, &uname)) return -errno; existing_strm = inode_get_stream(inode, STREAM_TYPE_DATA, uname); if (existing_strm) { tstr_put_utf16le(uname); return -EEXIST; } new_strm = inode_add_stream(inode, STREAM_TYPE_DATA, uname, NULL); tstr_put_utf16le(uname); if (!new_strm) return -errno; return 0; } else { /* Create a regular file, device node, named pipe, or socket. */ struct wim_dentry *dentry; int ret; if (!S_ISREG(mode) && !(wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA)) return -EPERM; ret = create_file(fuse_ctx, path, mode, rdev, &dentry); if (ret) return ret; touch_parent(dentry); return 0; } } static int wimfs_open(const char *path, struct fuse_file_info *fi) { struct wimfs_context *ctx = wimfs_get_context(); struct wim_dentry *dentry; struct wim_inode *inode; struct wim_inode_stream *strm; struct blob_descriptor *blob; struct wimfs_fd *fd; int ret; ret = wim_pathname_to_stream(ctx, path, 0, &dentry, &strm); if (ret) return ret; inode = dentry->d_inode; blob = stream_blob_resolved(strm); /* The data of the file being opened may be in the staging directory * (read-write mounts only) or in the WIM. If it's in the staging * directory, we need to open a native file descriptor for the * corresponding file. Otherwise, we can read the file data directly * from the WIM file if we are opening it read-only, but we need to * extract the data to the staging directory if we are opening it * writable. */ if (flags_writable(fi->flags) && (!blob || blob->blob_location != BLOB_IN_STAGING_FILE)) { ret = extract_blob_to_staging_dir(inode, strm, blob_size(blob), ctx); if (ret) return ret; blob = stream_blob_resolved(strm); } ret = alloc_wimfs_fd(inode, strm, &fd); if (ret) return ret; if (blob && blob->blob_location == BLOB_IN_STAGING_FILE) { int raw_fd; raw_fd = openat(blob->staging_dir_fd, blob->staging_file_name, (fi->flags & O_ACCMODE) | O_NOFOLLOW); if (raw_fd < 0) { close_wimfs_fd(fd); return -errno; } filedes_init(&fd->f_staging_fd, raw_fd); } fi->fh = (uintptr_t)fd; return 0; } static int wimfs_opendir(const char *path, struct fuse_file_info *fi) { WIMStruct *wim = wimfs_get_WIMStruct(); struct wim_inode *inode; struct wim_inode_stream *strm; struct wimfs_fd *fd; int ret; inode = wim_pathname_to_inode(wim, path); if (!inode) return -errno; if (!inode_is_directory(inode)) return -ENOTDIR; strm = inode_get_unnamed_data_stream(inode); if (!strm) return -ENOTDIR; ret = alloc_wimfs_fd(inode, strm, &fd); if (ret) return ret; fi->fh = (uintptr_t)fd; return 0; } static int wimfs_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { struct wimfs_fd *fd = WIMFS_FD(fi); const struct blob_descriptor *blob; ssize_t ret; blob = fd->f_blob; if (!blob) return 0; if (offset >= blob->size) return 0; if (size > blob->size - offset) size = blob->size - offset; if (!size) return 0; switch (blob->blob_location) { case BLOB_IN_WIM: if (read_partial_wim_blob_into_buf(blob, offset, size, buf)) ret = errno ? -errno : -EIO; else ret = size; break; case BLOB_IN_STAGING_FILE: ret = pread(fd->f_staging_fd.fd, buf, size, offset); if (ret < 0) ret = -errno; break; case BLOB_IN_ATTACHED_BUFFER: memcpy(buf, blob->attached_buffer + offset, size); ret = size; break; default: ret = -EINVAL; break; } return ret; } static int wimfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { struct wimfs_fd *fd = WIMFS_FD(fi); const struct wim_inode *inode; const struct wim_dentry *child; int ret; inode = fd->f_inode; ret = filler(buf, ".", NULL, 0); if (ret) return ret; ret = filler(buf, "..", NULL, 0); if (ret) return ret; for_inode_child(child, inode) { char *name; size_t name_nbytes; if (utf16le_to_tstr(child->d_name, child->d_name_nbytes, &name, &name_nbytes)) return -errno; ret = filler(buf, name, NULL, 0); FREE(name); if (ret) return ret; } return 0; } static int wimfs_readlink(const char *path, char *buf, size_t bufsize) { struct wimfs_context *ctx = wimfs_get_context(); const struct wim_inode *inode; int ret; inode = wim_pathname_to_inode(ctx->wim, path); if (!inode) return -errno; if (bufsize <= 0) return -EINVAL; ret = wim_inode_readlink(inode, buf, bufsize - 1, NULL, ctx->mountpoint_abspath, ctx->mountpoint_abspath_nchars); if (ret < 0) return ret; buf[ret] = '\0'; return 0; } /* We use this for both release() and releasedir(), since in both cases we * simply need to close the file descriptor. */ static int wimfs_release(const char *path, struct fuse_file_info *fi) { return close_wimfs_fd(WIMFS_FD(fi)); } static int wimfs_removexattr(const char *path, const char *name) { struct wimfs_context *ctx = wimfs_get_context(); struct wim_inode *inode; struct wim_inode_stream *strm; if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR)) return -ENOTSUP; if (strncmp(name, "user.", 5)) return -ENOATTR; name += 5; if (!*name) return -ENOATTR; /* Removing a named data stream. */ inode = wim_pathname_to_inode(ctx->wim, path); if (!inode) return -errno; strm = inode_get_data_stream_tstr(inode, name); if (!strm) return (errno == ENOENT) ? -ENOATTR : -errno; inode_remove_stream(inode, strm, ctx->wim->blob_table); return 0; } static int wimfs_rename(const char *from, const char *to) { return rename_wim_path(wimfs_get_WIMStruct(), from, to, WIMLIB_CASE_SENSITIVE, NULL); } static int wimfs_rmdir(const char *path) { WIMStruct *wim = wimfs_get_WIMStruct(); struct wim_dentry *dentry; dentry = get_dentry(wim, path, WIMLIB_CASE_SENSITIVE); if (!dentry) return -errno; if (!dentry_is_directory(dentry)) return -ENOTDIR; if (dentry_has_children(dentry)) return -ENOTEMPTY; touch_parent(dentry); remove_dentry(dentry, wim->blob_table); return 0; } static int wimfs_setxattr(const char *path, const char *name, const char *value, size_t size, int flags) { struct wimfs_context *ctx = wimfs_get_context(); struct wim_inode *inode; struct wim_inode_stream *strm; const utf16lechar *uname; int ret; if (!strncmp(name, "wimfs.", 6)) { /* Handle some magical extended attributes. These really should * be ioctls, but directory ioctls aren't supported until * libfuse 2.9, and even then they are broken. [Fixed by * libfuse commit e3b7d4c278a26520be63d99d6ea84b26906fe73d] */ name += 6; if (!strcmp(name, "unmount_info")) { if (!may_unmount_wimfs()) return -EPERM; if (size < sizeof(struct wimfs_unmount_info)) return -EINVAL; memcpy(&ctx->unmount_info, value, sizeof(struct wimfs_unmount_info)); return 0; } return -ENOATTR; } if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR)) return -ENOTSUP; if (strncmp(name, "user.", 5)) return -ENOATTR; name += 5; if (!*name) return -ENOATTR; /* Setting the contents of a named data stream. */ inode = wim_pathname_to_inode(ctx->wim, path); if (!inode) return -errno; ret = tstr_get_utf16le(name, &uname); if (ret) return -errno; strm = inode_get_stream(inode, STREAM_TYPE_DATA, uname); if (strm) { ret = -EEXIST; if (flags & XATTR_CREATE) goto out_put_uname; } else { ret = -ENOATTR; if (flags & XATTR_REPLACE) goto out_put_uname; } if (strm) { if (!inode_replace_stream_data(inode, strm, value, size, ctx->wim->blob_table)) { ret = -errno; goto out_put_uname; } } else { if (!inode_add_stream_with_data(inode, STREAM_TYPE_DATA, uname, value, size, ctx->wim->blob_table)) { ret = -errno; goto out_put_uname; } } ret = 0; out_put_uname: tstr_put_utf16le(uname); return ret; } static int wimfs_symlink(const char *to, const char *from) { struct fuse_context *fuse_ctx = fuse_get_context(); struct wimfs_context *wimfs_ctx = WIMFS_CTX(fuse_ctx); struct wim_dentry *dentry; int ret; ret = create_file(fuse_ctx, from, S_IFLNK | 0777, 0, &dentry); if (ret) return ret; ret = wim_inode_set_symlink(dentry->d_inode, to, wimfs_ctx->wim->blob_table); if (ret) { remove_dentry(dentry, wimfs_ctx->wim->blob_table); if (ret == WIMLIB_ERR_NOMEM) ret = -ENOMEM; else ret = -EINVAL; } else { touch_parent(dentry); } return ret; } static int wimfs_truncate(const char *path, off_t size) { const struct wimfs_context *ctx = wimfs_get_context(); struct wim_dentry *dentry; struct wim_inode_stream *strm; struct blob_descriptor *blob; int ret; int fd; ret = wim_pathname_to_stream(ctx, path, 0, &dentry, &strm); if (ret) return ret; blob = stream_blob_resolved(strm); if (!blob && !size) return 0; if (!blob || blob->blob_location != BLOB_IN_STAGING_FILE) { return extract_blob_to_staging_dir(dentry->d_inode, strm, size, ctx); } /* Truncate the staging file. */ fd = openat(blob->staging_dir_fd, blob->staging_file_name, O_WRONLY | O_NOFOLLOW); if (fd < 0) return -errno; ret = ftruncate(fd, size); if (close(fd) || ret) return -errno; blob->size = size; touch_inode(dentry->d_inode); return 0; } static int wimfs_unlink(const char *path) { const struct wimfs_context *ctx = wimfs_get_context(); struct wim_dentry *dentry; struct wim_inode_stream *strm; int ret; ret = wim_pathname_to_stream(ctx, path, 0, &dentry, &strm); if (ret) return ret; if (stream_is_named(strm)) { inode_remove_stream(dentry->d_inode, strm, ctx->wim->blob_table); } else { touch_parent(dentry); remove_dentry(dentry, ctx->wim->blob_table); } return 0; } #ifdef HAVE_UTIMENSAT /* * Change the timestamp on a file dentry. * * Note that alternate data streams do not have their own timestamps. */ static int wimfs_utimens(const char *path, const struct timespec tv[2]) { WIMStruct *wim = wimfs_get_WIMStruct(); struct wim_inode *inode; inode = wim_pathname_to_inode(wim, path); if (!inode) return -errno; if (tv[0].tv_nsec != UTIME_OMIT) { if (tv[0].tv_nsec == UTIME_NOW) inode->i_last_access_time = now_as_wim_timestamp(); else inode->i_last_access_time = timespec_to_wim_timestamp(&tv[0]); } if (tv[1].tv_nsec != UTIME_OMIT) { if (tv[1].tv_nsec == UTIME_NOW) inode->i_last_write_time = now_as_wim_timestamp(); else inode->i_last_write_time = timespec_to_wim_timestamp(&tv[1]); } return 0; } #else /* HAVE_UTIMENSAT */ static int wimfs_utime(const char *path, struct utimbuf *times) { WIMStruct *wim = wimfs_get_WIMStruct(); struct wim_inode *inode; inode = wim_pathname_to_inode(wim, path); if (!inode) return -errno; inode->i_last_access_time = time_t_to_wim_timestamp(times->actime); inode->i_last_write_time = time_t_to_wim_timestamp(times->modtime); return 0; } #endif /* !HAVE_UTIMENSAT */ static int wimfs_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { struct wimfs_fd *fd = WIMFS_FD(fi); ssize_t ret; ret = pwrite(fd->f_staging_fd.fd, buf, size, offset); if (ret < 0) return -errno; if (offset + size > fd->f_blob->size) fd->f_blob->size = offset + size; touch_inode(fd->f_inode); return ret; } static const struct fuse_operations wimfs_operations = { .chmod = wimfs_chmod, .chown = wimfs_chown, .fgetattr = wimfs_fgetattr, .ftruncate = wimfs_ftruncate, .getattr = wimfs_getattr, .getxattr = wimfs_getxattr, .link = wimfs_link, .listxattr = wimfs_listxattr, .mkdir = wimfs_mkdir, .mknod = wimfs_mknod, .open = wimfs_open, .opendir = wimfs_opendir, .read = wimfs_read, .readdir = wimfs_readdir, .readlink = wimfs_readlink, .release = wimfs_release, .releasedir = wimfs_release, .removexattr = wimfs_removexattr, .rename = wimfs_rename, .rmdir = wimfs_rmdir, .setxattr = wimfs_setxattr, .symlink = wimfs_symlink, .truncate = wimfs_truncate, .unlink = wimfs_unlink, #ifdef HAVE_UTIMENSAT .utimens = wimfs_utimens, #else .utime = wimfs_utime, #endif .write = wimfs_write, /* We keep track of file descriptor structures (struct wimfs_fd), so * there is no need to have the file path provided on operations such as * read(). */ #if FUSE_MAJOR_VERSION > 2 || (FUSE_MAJOR_VERSION == 2 && FUSE_MINOR_VERSION >= 8) .flag_nullpath_ok = 1, #endif #if FUSE_MAJOR_VERSION > 2 || (FUSE_MAJOR_VERSION == 2 && FUSE_MINOR_VERSION >= 9) .flag_nopath = 1, .flag_utime_omit_ok = 1, #endif }; /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_mount_image(WIMStruct *wim, int image, const char *dir, int mount_flags, const char *staging_dir) { int ret; struct wim_image_metadata *imd; struct wimfs_context ctx; char *fuse_argv[16]; int fuse_argc; if (!wim || !dir || !*dir) return WIMLIB_ERR_INVALID_PARAM; if (mount_flags & ~(WIMLIB_MOUNT_FLAG_READWRITE | WIMLIB_MOUNT_FLAG_DEBUG | WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE | WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR | WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS | WIMLIB_MOUNT_FLAG_UNIX_DATA | WIMLIB_MOUNT_FLAG_ALLOW_OTHER)) return WIMLIB_ERR_INVALID_PARAM; /* For read-write mount, check for write access to the WIM. */ if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) { if (!wim->filename) return WIMLIB_ERR_NO_FILENAME; ret = can_modify_wim(wim); if (ret) return ret; } /* Select the image to mount. */ ret = select_wim_image(wim, image); if (ret) return ret; /* Get the metadata for the image to mount. */ imd = wim_get_current_image_metadata(wim); /* To avoid complicating things, we don't support mounting images to * which in-memory modifications have already been made. */ if (is_image_dirty(imd)) { ERROR("Cannot mount a modified WIM image!"); return WIMLIB_ERR_INVALID_PARAM; } if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) { if (imd->refcnt > 1) return WIMLIB_ERR_IMAGE_HAS_MULTIPLE_REFERENCES; ret = lock_wim_for_append(wim); if (ret) return ret; } if (wim_has_solid_resources(wim)) { WARNING("Mounting a WIM file containing solid-compressed data; " "file access may be slow."); } /* If the user did not specify an interface for accessing named * data streams, use the default (extended attributes). */ if (!(mount_flags & (WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE | WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR | WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS))) mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR; /* Start initializing the wimfs_context. */ memset(&ctx, 0, sizeof(struct wimfs_context)); ctx.wim = wim; ctx.mount_flags = mount_flags; if (mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS) ctx.default_lookup_flags = LOOKUP_FLAG_ADS_OK; /* For read-write mounts, create the staging directory, save a reference * to the image's metadata resource, and mark the image dirty. */ if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) { ret = make_staging_dir(&ctx, staging_dir); if (ret) goto out; ret = WIMLIB_ERR_NOMEM; ctx.metadata_resource = clone_blob_descriptor( imd->metadata_blob); if (!ctx.metadata_resource) goto out; mark_image_dirty(imd); } ctx.owner_uid = getuid(); ctx.owner_gid = getgid(); /* Number the inodes in the mounted image sequentially and initialize * the file descriptor arrays */ prepare_inodes(&ctx); /* Save the absolute path to the mountpoint directory. */ ctx.mountpoint_abspath = realpath(dir, NULL); if (ctx.mountpoint_abspath) ctx.mountpoint_abspath_nchars = strlen(ctx.mountpoint_abspath); /* Build the FUSE command line. */ fuse_argc = 0; fuse_argv[fuse_argc++] = "wimlib"; fuse_argv[fuse_argc++] = (char *)dir; /* Disable multi-threaded operation. */ fuse_argv[fuse_argc++] = "-s"; /* Enable FUSE debug mode (don't fork) if requested by the user. */ if (mount_flags & WIMLIB_MOUNT_FLAG_DEBUG) fuse_argv[fuse_argc++] = "-d"; /* * Build the FUSE mount options: * * use_ino * FUSE will use the inode numbers we provide. We want this, * because we have inodes and will number them ourselves. * * subtype=wimfs * Name for our filesystem (main type is "fuse"). * * hard_remove * If an open file is unlinked, unlink it for real rather than * renaming it to a hidden file. Our code supports this; an * unlinked inode is retained until all its file descriptors have * been closed. * * default_permissions * FUSE will perform permission checking. Useful when * WIMLIB_MOUNT_FLAG_UNIX_DATA is provided and the WIM image * contains the UNIX permissions for each file. * * kernel_cache * Cache the contents of files. This will speed up repeated access * to files on a mounted WIM image, since they won't need to be * decompressed repeatedly. This option is valid because data in * the WIM image should never be changed externally. (Although, if * someone really wanted to they could modify the WIM file or mess * with the staging directory; but then they're asking for * trouble.) * * entry_timeout=1000000000 * Cache positive name lookups indefinitely, since names can only * be added, removed, or modified through the mounted filesystem * itself. * * negative_timeout=1000000000 * Cache negative name lookups indefinitely, since names can only * be added, removed, or modified through the mounted filesystem * itself. * * attr_timeout=0 * Don't cache file/directory attributes. This is needed as a * workaround for the fact that when caching attributes, the high * level interface to libfuse considers a file which has several * hard-linked names as several different files. (Otherwise, we * could cache our file/directory attributes indefinitely, since * they can only be changed through the mounted filesystem itself.) */ char optstring[256] = "use_ino" ",subtype=wimfs" ",hard_remove" ",default_permissions" ",kernel_cache" ",entry_timeout=1000000000" ",negative_timeout=1000000000" ",attr_timeout=0" ; fuse_argv[fuse_argc++] = "-o"; fuse_argv[fuse_argc++] = optstring; if (!(mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)) strcat(optstring, ",ro"); if (mount_flags & WIMLIB_MOUNT_FLAG_ALLOW_OTHER) strcat(optstring, ",allow_other"); fuse_argv[fuse_argc] = NULL; /* Mount our filesystem. */ ret = fuse_main(fuse_argc, fuse_argv, &wimfs_operations, &ctx); /* Cleanup and return. */ if (ret) ret = WIMLIB_ERR_FUSE; out: FREE(ctx.mountpoint_abspath); free_blob_descriptor(ctx.metadata_resource); if (ctx.staging_dir_name) delete_staging_dir(&ctx); unlock_wim_for_append(wim); return ret; } struct commit_progress_thread_args { mqd_t mq; wimlib_progress_func_t progfunc; void *progctx; }; static void * commit_progress_thread_proc(void *_args) { struct commit_progress_thread_args *args = _args; struct commit_progress_report report; ssize_t ret; for (;;) { ret = mq_receive(args->mq, (char *)&report, sizeof(report), NULL); if (ret == sizeof(report)) { call_progress(args->progfunc, report.msg, &report.info, args->progctx); } else { if (ret == 0 || (ret < 0 && errno != EINTR)) break; } } return NULL; } static void generate_message_queue_name(char name[WIMFS_MQUEUE_NAME_LEN + 1]) { name[0] = '/'; memcpy(name + 1, "wimfs-", 6); get_random_alnum_chars(name + 7, WIMFS_MQUEUE_NAME_LEN - 7); name[WIMFS_MQUEUE_NAME_LEN] = '\0'; } static mqd_t create_message_queue(const char *name) { bool am_root; mode_t umask_save; mode_t mode; struct mq_attr attr; mqd_t mq; memset(&attr, 0, sizeof(attr)); attr.mq_maxmsg = 8; attr.mq_msgsize = sizeof(struct commit_progress_report); am_root = (geteuid() == 0); if (am_root) { /* Filesystem mounted as normal user with --allow-other should * be able to send messages to root user, if they're doing the * unmount. */ umask_save = umask(0); mode = 0666; } else { mode = 0600; } mq = mq_open(name, O_RDWR | O_CREAT | O_EXCL, mode, &attr); if (am_root) umask(umask_save); return mq; } /* Unmount a read-only or read-write mounted WIM image. */ static int do_unmount(const char *dir) { int status; ssize_t len; len = getxattr(dir, "wimfs.unmount", &status, sizeof(int)); if (len == sizeof(int)) return status; else if (len < 0 && (errno == EACCES || errno == EPERM)) return WIMLIB_ERR_NOT_PERMITTED_TO_UNMOUNT; else return WIMLIB_ERR_NOT_A_MOUNTPOINT; } static int set_unmount_info(const char *dir, const struct wimfs_unmount_info *unmount_info) { if (!setxattr(dir, "wimfs.unmount_info", unmount_info, sizeof(struct wimfs_unmount_info), 0)) return 0; else if (errno == EROFS) return 0; else if (errno == EACCES || errno == EPERM) return WIMLIB_ERR_NOT_PERMITTED_TO_UNMOUNT; else return WIMLIB_ERR_NOT_A_MOUNTPOINT; } static int do_unmount_discard(const char *dir) { int ret; struct wimfs_unmount_info unmount_info; memset(&unmount_info, 0, sizeof(unmount_info)); ret = set_unmount_info(dir, &unmount_info); if (ret) return ret; return do_unmount(dir); } /* Unmount a read-write mounted WIM image, committing the changes. */ static int do_unmount_commit(const char *dir, int unmount_flags, wimlib_progress_func_t progfunc, void *progctx) { struct wimfs_unmount_info unmount_info; mqd_t mq; struct commit_progress_thread_args args; pthread_t commit_progress_tid; int ret; memset(&unmount_info, 0, sizeof(unmount_info)); unmount_info.unmount_flags = unmount_flags; /* The current thread will be stuck in getxattr() until the image is * committed. Create a thread to handle the progress messages. */ if (progfunc) { generate_message_queue_name(unmount_info.mq_name); mq = create_message_queue(unmount_info.mq_name); if (mq == (mqd_t)-1) { ERROR_WITH_ERRNO("Can't create POSIX message queue"); return WIMLIB_ERR_MQUEUE; } args.mq = mq; args.progfunc = progfunc; args.progctx = progctx; ret = pthread_create(&commit_progress_tid, NULL, commit_progress_thread_proc, &args); if (ret) { errno = ret; ERROR_WITH_ERRNO("Can't create thread"); ret = WIMLIB_ERR_NOMEM; goto out_delete_mq; } unmount_info.unmount_flags |= WIMLIB_UNMOUNT_FLAG_SEND_PROGRESS; } ret = set_unmount_info(dir, &unmount_info); if (!ret) ret = do_unmount(dir); if (progfunc) { /* Terminate the progress thread. */ char empty[1]; mq_send(mq, empty, 0, 1); pthread_join(commit_progress_tid, NULL); } out_delete_mq: if (progfunc) { mq_close(mq); mq_unlink(unmount_info.mq_name); } return ret; } static int begin_unmount(const char *dir, int unmount_flags, int *mount_flags_ret, wimlib_progress_func_t progfunc, void *progctx) { int mount_flags; int mounted_image; int wim_filename_len; union wimlib_progress_info progress; if (getxattr(dir, "wimfs.mount_flags", &mount_flags, sizeof(int)) != sizeof(int)) return WIMLIB_ERR_NOT_A_MOUNTPOINT; *mount_flags_ret = mount_flags; if (!progfunc) return 0; if (getxattr(dir, "wimfs.mounted_image", &mounted_image, sizeof(int)) != sizeof(int)) return WIMLIB_ERR_NOT_A_MOUNTPOINT; wim_filename_len = getxattr(dir, "wimfs.wim_filename", NULL, 0); if (wim_filename_len < 0) return WIMLIB_ERR_NOT_A_MOUNTPOINT; char wim_filename[wim_filename_len + 1]; if (getxattr(dir, "wimfs.wim_filename", wim_filename, wim_filename_len) != wim_filename_len) return WIMLIB_ERR_NOT_A_MOUNTPOINT; wim_filename[wim_filename_len] = '\0'; progress.unmount.mountpoint = dir; progress.unmount.mounted_wim = wim_filename; progress.unmount.mounted_image = mounted_image; progress.unmount.mount_flags = mount_flags; progress.unmount.unmount_flags = unmount_flags; return call_progress(progfunc, WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN, &progress, progctx); } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_unmount_image_with_progress(const char *dir, int unmount_flags, wimlib_progress_func_t progfunc, void *progctx) { int mount_flags; int ret; ret = wimlib_global_init(0); if (ret) return ret; if (unmount_flags & ~(WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY | WIMLIB_UNMOUNT_FLAG_COMMIT | WIMLIB_UNMOUNT_FLAG_REBUILD | WIMLIB_UNMOUNT_FLAG_RECOMPRESS | WIMLIB_UNMOUNT_FLAG_FORCE | WIMLIB_UNMOUNT_FLAG_NEW_IMAGE)) return WIMLIB_ERR_INVALID_PARAM; ret = begin_unmount(dir, unmount_flags, &mount_flags, progfunc, progctx); if (ret) return ret; if ((unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) && (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)) return do_unmount_commit(dir, unmount_flags, progfunc, progctx); else return do_unmount_discard(dir); } #else /* WITH_FUSE */ static int mount_unsupported_error(void) { #if defined(__WIN32__) ERROR("Sorry-- Mounting WIM images is not supported on Windows!"); #else ERROR("wimlib was compiled with --without-fuse, which disables support " "for mounting WIMs."); #endif return WIMLIB_ERR_UNSUPPORTED; } WIMLIBAPI int wimlib_unmount_image_with_progress(const tchar *dir, int unmount_flags, wimlib_progress_func_t progfunc, void *progctx) { return mount_unsupported_error(); } WIMLIBAPI int wimlib_mount_image(WIMStruct *wim, int image, const tchar *dir, int mount_flags, const tchar *staging_dir) { return mount_unsupported_error(); } #endif /* !WITH_FUSE */ WIMLIBAPI int wimlib_unmount_image(const tchar *dir, int unmount_flags) { return wimlib_unmount_image_with_progress(dir, unmount_flags, NULL, NULL); } wimlib-1.13.1/src/split.c0000644000175000017500000001551513272231267012103 00000000000000/* * split.c * * Split a WIM file into parts. */ /* * Copyright (C) 2012, 2013 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib.h" #include "wimlib/alloca.h" #include "wimlib/blob_table.h" #include "wimlib/error.h" #include "wimlib/list.h" #include "wimlib/metadata.h" #include "wimlib/paths.h" #include "wimlib/progress.h" #include "wimlib/resource.h" #include "wimlib/wim.h" #include "wimlib/write.h" struct swm_part_info { struct list_head blob_list; u64 size; }; static void copy_part_info(struct swm_part_info *dst, struct swm_part_info *src) { list_replace(&src->blob_list, &dst->blob_list); dst->size = src->size; } struct swm_info { struct swm_part_info *parts; unsigned num_parts; unsigned num_alloc_parts; u64 total_bytes; u64 max_part_size; }; static int write_split_wim(WIMStruct *orig_wim, const tchar *swm_name, struct swm_info *swm_info, int write_flags) { size_t swm_name_len; tchar *swm_name_buf; const tchar *dot; tchar *swm_suffix; size_t swm_base_name_len; union wimlib_progress_info progress; unsigned part_number; int ret; u8 guid[GUID_SIZE]; swm_name_len = tstrlen(swm_name); swm_name_buf = alloca((swm_name_len + 20) * sizeof(tchar)); tstrcpy(swm_name_buf, swm_name); dot = tstrrchr(path_basename(swm_name_buf), T('.')); if (dot) { swm_base_name_len = dot - swm_name_buf; swm_suffix = alloca((tstrlen(dot) + 1) * sizeof(tchar)); tstrcpy(swm_suffix, dot); } else { swm_base_name_len = swm_name_len; swm_suffix = alloca(1 * sizeof(tchar)); swm_suffix[0] = T('\0'); } progress.split.completed_bytes = 0; progress.split.total_bytes = 0; for (part_number = 1; part_number <= swm_info->num_parts; part_number++) progress.split.total_bytes += swm_info->parts[part_number - 1].size; progress.split.total_parts = swm_info->num_parts; generate_guid(guid); for (part_number = 1; part_number <= swm_info->num_parts; part_number++) { int part_write_flags; wimlib_progress_func_t progfunc; if (part_number != 1) { tsprintf(swm_name_buf + swm_base_name_len, T("%u%"TS), part_number, swm_suffix); } progress.split.cur_part_number = part_number; progress.split.part_name = swm_name_buf; ret = call_progress(orig_wim->progfunc, WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART, &progress, orig_wim->progctx); if (ret) return ret; part_write_flags = write_flags; part_write_flags |= WIMLIB_WRITE_FLAG_USE_EXISTING_TOTALBYTES; if (part_number != 1) part_write_flags |= WIMLIB_WRITE_FLAG_NO_METADATA; progfunc = orig_wim->progfunc; orig_wim->progfunc = NULL; ret = write_wim_part(orig_wim, progress.split.part_name, WIMLIB_ALL_IMAGES, part_write_flags, 1, part_number, swm_info->num_parts, &swm_info->parts[part_number - 1].blob_list, guid); orig_wim->progfunc = progfunc; if (ret) return ret; progress.split.completed_bytes += swm_info->parts[part_number - 1].size; ret = call_progress(orig_wim->progfunc, WIMLIB_PROGRESS_MSG_SPLIT_END_PART, &progress, orig_wim->progctx); if (ret) return ret; } return 0; } static int start_new_swm_part(struct swm_info *swm_info) { if (swm_info->num_parts == swm_info->num_alloc_parts) { struct swm_part_info *parts; size_t num_alloc_parts = swm_info->num_alloc_parts; num_alloc_parts += 8; parts = MALLOC(num_alloc_parts * sizeof(parts[0])); if (!parts) return WIMLIB_ERR_NOMEM; for (unsigned i = 0; i < swm_info->num_parts; i++) copy_part_info(&parts[i], &swm_info->parts[i]); FREE(swm_info->parts); swm_info->parts = parts; swm_info->num_alloc_parts = num_alloc_parts; } swm_info->num_parts++; INIT_LIST_HEAD(&swm_info->parts[swm_info->num_parts - 1].blob_list); swm_info->parts[swm_info->num_parts - 1].size = 0; return 0; } static int add_blob_to_swm(struct blob_descriptor *blob, void *_swm_info) { struct swm_info *swm_info = _swm_info; u64 blob_stored_size; int ret; if (blob->blob_location == BLOB_IN_WIM) blob_stored_size = blob->rdesc->size_in_wim; else blob_stored_size = blob->size; /* Start the next part if adding this blob exceeds the maximum part * size, UNLESS the blob is metadata or if no blobs at all have been * added to the current part. */ if ((swm_info->parts[swm_info->num_parts - 1].size + blob_stored_size >= swm_info->max_part_size) && !(blob->is_metadata || swm_info->parts[swm_info->num_parts - 1].size == 0)) { ret = start_new_swm_part(swm_info); if (ret) return ret; } swm_info->parts[swm_info->num_parts - 1].size += blob_stored_size; if (!blob->is_metadata) { list_add_tail(&blob->write_blobs_list, &swm_info->parts[swm_info->num_parts - 1].blob_list); } swm_info->total_bytes += blob_stored_size; return 0; } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_split(WIMStruct *wim, const tchar *swm_name, u64 part_size, int write_flags) { struct swm_info swm_info; unsigned i; int ret; if (swm_name == NULL || swm_name[0] == T('\0') || part_size == 0) return WIMLIB_ERR_INVALID_PARAM; if (write_flags & ~WIMLIB_WRITE_MASK_PUBLIC) return WIMLIB_ERR_INVALID_PARAM; if (!wim_has_metadata(wim)) return WIMLIB_ERR_METADATA_NOT_FOUND; if (wim_has_solid_resources(wim)) { ERROR("Splitting of WIM containing solid resources is not supported.\n" " Export it in non-solid format first."); return WIMLIB_ERR_UNSUPPORTED; } for (i = 0; i < wim->hdr.image_count; i++) { if (!is_image_unchanged_from_wim(wim->image_metadata[i], wim)) { ERROR("Only an unmodified, on-disk WIM file can be split."); return WIMLIB_ERR_UNSUPPORTED; } } memset(&swm_info, 0, sizeof(swm_info)); swm_info.max_part_size = part_size; ret = start_new_swm_part(&swm_info); if (ret) goto out_free_swm_info; for (i = 0; i < wim->hdr.image_count; i++) { ret = add_blob_to_swm(wim->image_metadata[i]->metadata_blob, &swm_info); if (ret) goto out_free_swm_info; } ret = for_blob_in_table_sorted_by_sequential_order(wim->blob_table, add_blob_to_swm, &swm_info); if (ret) goto out_free_swm_info; ret = write_split_wim(wim, swm_name, &swm_info, write_flags); out_free_swm_info: FREE(swm_info.parts); return ret; } wimlib-1.13.1/src/integrity.c0000644000175000017500000003216313160354224012757 00000000000000/* * integrity.c * * WIM files can optionally contain a table of SHA1 message digests at the end, * one digest for each chunk of the file of some specified size (often 10 MB). * This file implements the checking and writing of this table. */ /* * Copyright (C) 2012-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib/assert.h" #include "wimlib/endianness.h" #include "wimlib/error.h" #include "wimlib/file_io.h" #include "wimlib/integrity.h" #include "wimlib/progress.h" #include "wimlib/resource.h" #include "wimlib/sha1.h" #include "wimlib/wim.h" #include "wimlib/write.h" /* Size, in bytes, of each SHA1-summed chunk, when wimlib writes integrity * information. */ #define INTEGRITY_CHUNK_SIZE 10485760 /* Only use a different chunk size for compatibility with an existing integrity * table if the chunk size is between these two numbers. */ #define INTEGRITY_MIN_CHUNK_SIZE 4096 #define INTEGRITY_MAX_CHUNK_SIZE 134217728 struct integrity_table { u32 size; u32 num_entries; u32 chunk_size; u8 sha1sums[][20]; } _packed_attribute; static int calculate_chunk_sha1(struct filedes *in_fd, size_t this_chunk_size, off_t offset, u8 sha1_md[]) { u8 buf[BUFFER_SIZE]; SHA_CTX ctx; size_t bytes_remaining; size_t bytes_to_read; int ret; bytes_remaining = this_chunk_size; sha1_init(&ctx); do { bytes_to_read = min(bytes_remaining, sizeof(buf)); ret = full_pread(in_fd, buf, bytes_to_read, offset); if (ret) { ERROR_WITH_ERRNO("Read error while calculating " "integrity checksums"); return ret; } sha1_update(&ctx, buf, bytes_to_read); bytes_remaining -= bytes_to_read; offset += bytes_to_read; } while (bytes_remaining); sha1_final(sha1_md, &ctx); return 0; } /* * read_integrity_table: - Reads the integrity table from a WIM file. * * @wim: * WIMStruct for the WIM file; @wim->hdr.integrity_table_reshdr specifies * the location of the integrity table. @wim->in_fd is expected to be a * seekable file descriptor to the WIM file opened for reading. * * @num_checked_bytes: * Number of bytes of data that should be checked by the integrity table. * * @table_ret: * On success, a pointer to an in-memory structure containing the integrity * information is written to this location. * * Return values: * WIMLIB_ERR_SUCCESS (0) * WIMLIB_ERR_INVALID_INTEGRITY_TABLE * WIMLIB_ERR_NOMEM * WIMLIB_ERR_READ * WIMLIB_ERR_UNEXPECTED_END_OF_FILE */ int read_integrity_table(WIMStruct *wim, u64 num_checked_bytes, struct integrity_table **table_ret) { void *buf; struct integrity_table *table; int ret; STATIC_ASSERT(sizeof(struct integrity_table) == 12); if (wim->hdr.integrity_table_reshdr.uncompressed_size < 12) return WIMLIB_ERR_INVALID_INTEGRITY_TABLE; ret = wim_reshdr_to_data(&wim->hdr.integrity_table_reshdr, wim, &buf); if (ret) return ret; table = buf; table->size = le32_to_cpu((_force_attr le32)table->size); table->num_entries = le32_to_cpu((_force_attr le32)table->num_entries); table->chunk_size = le32_to_cpu((_force_attr le32)table->chunk_size); if (table->size != wim->hdr.integrity_table_reshdr.uncompressed_size || table->size != (u64)table->num_entries * SHA1_HASH_SIZE + 12 || table->chunk_size == 0 || table->num_entries != DIV_ROUND_UP(num_checked_bytes, table->chunk_size)) { FREE(table); return WIMLIB_ERR_INVALID_INTEGRITY_TABLE; } *table_ret = table; return 0; } /* * calculate_integrity_table(): * * Calculates an integrity table for the data in a file beginning at offset 208 * (WIM_HEADER_DISK_SIZE). * * @in_fd: * File descriptor for the file to be checked, opened for reading. Does * not need to be at any specific location in the file. * * @new_check_end: * Offset of byte after the last byte to be checked. * * @old_table: * If non-NULL, a pointer to the table containing the previously calculated * integrity data for a prefix of this file. * * @old_check_end: * If @old_table is non-NULL, the byte after the last byte that was checked * in the old table. Must be less than or equal to new_check_end. * * @integrity_table_ret: * On success, a pointer to the calculated integrity table is written into * this location. * * Return values: * WIMLIB_ERR_SUCCESS (0) * WIMLIB_ERR_NOMEM * WIMLIB_ERR_READ * WIMLIB_ERR_UNEXPECTED_END_OF_FILE */ static int calculate_integrity_table(struct filedes *in_fd, off_t new_check_end, const struct integrity_table *old_table, off_t old_check_end, struct integrity_table **integrity_table_ret, wimlib_progress_func_t progfunc, void *progctx) { int ret; size_t chunk_size = INTEGRITY_CHUNK_SIZE; /* If an old table is provided, set the chunk size to be compatible with * the old chunk size, unless the old chunk size was weird. */ if (old_table != NULL) { if (old_table->num_entries == 0 || old_table->chunk_size < INTEGRITY_MIN_CHUNK_SIZE || old_table->chunk_size > INTEGRITY_MAX_CHUNK_SIZE) old_table = NULL; else chunk_size = old_table->chunk_size; } u64 old_check_bytes = old_check_end - WIM_HEADER_DISK_SIZE; u64 new_check_bytes = new_check_end - WIM_HEADER_DISK_SIZE; u32 old_num_chunks = DIV_ROUND_UP(old_check_bytes, chunk_size); u32 new_num_chunks = DIV_ROUND_UP(new_check_bytes, chunk_size); size_t old_last_chunk_size = MODULO_NONZERO(old_check_bytes, chunk_size); size_t new_last_chunk_size = MODULO_NONZERO(new_check_bytes, chunk_size); size_t new_table_size = 12 + new_num_chunks * SHA1_HASH_SIZE; struct integrity_table *new_table = MALLOC(new_table_size); if (!new_table) return WIMLIB_ERR_NOMEM; new_table->num_entries = new_num_chunks; new_table->size = new_table_size; new_table->chunk_size = chunk_size; u64 offset = WIM_HEADER_DISK_SIZE; union wimlib_progress_info progress; progress.integrity.total_bytes = new_check_bytes; progress.integrity.total_chunks = new_num_chunks; progress.integrity.completed_chunks = 0; progress.integrity.completed_bytes = 0; progress.integrity.chunk_size = chunk_size; progress.integrity.filename = NULL; ret = call_progress(progfunc, WIMLIB_PROGRESS_MSG_CALC_INTEGRITY, &progress, progctx); if (ret) goto out_free_new_table; for (u32 i = 0; i < new_num_chunks; i++) { size_t this_chunk_size; if (i == new_num_chunks - 1) this_chunk_size = new_last_chunk_size; else this_chunk_size = chunk_size; if (old_table && ((this_chunk_size == chunk_size && i < old_num_chunks - 1) || (i == old_num_chunks - 1 && this_chunk_size == old_last_chunk_size))) { /* Can use SHA1 message digest from old integrity table * */ copy_hash(new_table->sha1sums[i], old_table->sha1sums[i]); } else { /* Calculate the SHA1 message digest of this chunk */ ret = calculate_chunk_sha1(in_fd, this_chunk_size, offset, new_table->sha1sums[i]); if (ret) goto out_free_new_table; } offset += this_chunk_size; progress.integrity.completed_chunks++; progress.integrity.completed_bytes += this_chunk_size; ret = call_progress(progfunc, WIMLIB_PROGRESS_MSG_CALC_INTEGRITY, &progress, progctx); if (ret) goto out_free_new_table; } *integrity_table_ret = new_table; return 0; out_free_new_table: FREE(new_table); return ret; } /* * write_integrity_table(): * * Writes a WIM integrity table (a list of SHA1 message digests of raw 10 MiB * chunks of the file). * * This function can optionally re-use entries from an older integrity table. * To do this, specify old_blob_table_end and old_table. * * On success, @wim->out_hdr.integrity_table_reshdr will be filled in with * information about the integrity table that was written. * * @wim: * WIMStruct for the WIM file. @wim->out_fd must be a seekable descriptor * to the new WIM file, opened read-write, positioned at the location at * which the integrity table is to be written. * * @new_blob_table_end: * The offset of the byte directly following the blob table in the WIM * being written. * * @old_blob_table_end: * If nonzero, the offset of the byte directly following the old blob table * in the WIM. * * @old_table * Pointer to the old integrity table read into memory, or NULL if not * specified. */ int write_integrity_table(WIMStruct *wim, off_t new_blob_table_end, off_t old_blob_table_end, struct integrity_table *old_table) { struct integrity_table *new_table; int ret; u32 new_table_size; wimlib_assert(old_blob_table_end <= new_blob_table_end); ret = calculate_integrity_table(&wim->out_fd, new_blob_table_end, old_table, old_blob_table_end, &new_table, wim->progfunc, wim->progctx); if (ret) return ret; new_table_size = new_table->size; new_table->size = (_force_attr u32)cpu_to_le32(new_table->size); new_table->num_entries = (_force_attr u32)cpu_to_le32(new_table->num_entries); new_table->chunk_size = (_force_attr u32)cpu_to_le32(new_table->chunk_size); ret = write_wim_resource_from_buffer(new_table, new_table_size, false, &wim->out_fd, WIMLIB_COMPRESSION_TYPE_NONE, 0, &wim->out_hdr.integrity_table_reshdr, NULL, 0); FREE(new_table); return ret; } /* * verify_integrity(): * * Checks a WIM for consistency with the integrity table. * * @in_fd: * File descriptor to the WIM file, opened for reading. * * @table: * The integrity table for the WIM, read into memory. * * @bytes_to_check: * Number of bytes in the WIM that need to be checked (offset of end of the * blob table minus offset of end of the header). * * Returns: * > 0 (WIMLIB_ERR_READ, WIMLIB_ERR_UNEXPECTED_END_OF_FILE) on error * 0 (WIM_INTEGRITY_OK) if the integrity was checked successfully and there * were no inconsistencies. * -1 (WIM_INTEGRITY_NOT_OK) if the WIM failed the integrity check. */ static int verify_integrity(struct filedes *in_fd, const tchar *filename, const struct integrity_table *table, u64 bytes_to_check, wimlib_progress_func_t progfunc, void *progctx) { int ret; u64 offset = WIM_HEADER_DISK_SIZE; u8 sha1_md[SHA1_HASH_SIZE]; union wimlib_progress_info progress; progress.integrity.total_bytes = bytes_to_check; progress.integrity.total_chunks = table->num_entries; progress.integrity.completed_chunks = 0; progress.integrity.completed_bytes = 0; progress.integrity.chunk_size = table->chunk_size; progress.integrity.filename = filename; ret = call_progress(progfunc, WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY, &progress, progctx); if (ret) return ret; for (u32 i = 0; i < table->num_entries; i++) { size_t this_chunk_size; if (i == table->num_entries - 1) this_chunk_size = MODULO_NONZERO(bytes_to_check, table->chunk_size); else this_chunk_size = table->chunk_size; ret = calculate_chunk_sha1(in_fd, this_chunk_size, offset, sha1_md); if (ret) return ret; if (!hashes_equal(sha1_md, table->sha1sums[i])) return WIM_INTEGRITY_NOT_OK; offset += this_chunk_size; progress.integrity.completed_chunks++; progress.integrity.completed_bytes += this_chunk_size; ret = call_progress(progfunc, WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY, &progress, progctx); if (ret) return ret; } return WIM_INTEGRITY_OK; } /* * check_wim_integrity(): * * Verifies the integrity of the WIM by making sure the SHA1 message digests of * ~10 MiB chunks of the WIM match up with the values given in the integrity * table. * * @wim: * The WIM, opened for reading. * * Returns: * > 0 (WIMLIB_ERR_INVALID_INTEGRITY_TABLE, WIMLIB_ERR_READ, * WIMLIB_ERR_UNEXPECTED_END_OF_FILE) on error * 0 (WIM_INTEGRITY_OK) if the integrity was checked successfully and there * were no inconsistencies. * -1 (WIM_INTEGRITY_NOT_OK) if the WIM failed the integrity check. * -2 (WIM_INTEGRITY_NONEXISTENT) if the WIM contains no integrity * information. */ int check_wim_integrity(WIMStruct *wim) { int ret; u64 bytes_to_check; struct integrity_table *table; u64 end_blob_table_offset; if (!wim_has_integrity_table(wim)) return WIM_INTEGRITY_NONEXISTENT; end_blob_table_offset = wim->hdr.blob_table_reshdr.offset_in_wim + wim->hdr.blob_table_reshdr.size_in_wim; if (end_blob_table_offset < WIM_HEADER_DISK_SIZE) { ERROR("WIM blob table ends before WIM header ends!"); return WIMLIB_ERR_INVALID_INTEGRITY_TABLE; } bytes_to_check = end_blob_table_offset - WIM_HEADER_DISK_SIZE; ret = read_integrity_table(wim, bytes_to_check, &table); if (ret) return ret; ret = verify_integrity(&wim->in_fd, wim->filename, table, bytes_to_check, wim->progfunc, wim->progctx); FREE(table); return ret; } wimlib-1.13.1/src/lzx_common.c0000644000175000017500000002355413160354225013133 00000000000000/* * lzx_common.c - Common code for LZX compression and decompression. */ /* * Copyright (C) 2012-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #ifdef __SSE2__ # include #endif #ifdef __AVX2__ # include #endif #include "wimlib/bitops.h" #include "wimlib/endianness.h" #include "wimlib/lzx_common.h" #include "wimlib/unaligned.h" #include "wimlib/util.h" /* Mapping: offset slot => first match offset that uses that offset slot. * The offset slots for repeat offsets map to "fake" offsets < 1. */ const s32 lzx_offset_slot_base[LZX_MAX_OFFSET_SLOTS + 1] = { -2 , -1 , 0 , 1 , 2 , /* 0 --- 4 */ 4 , 6 , 10 , 14 , 22 , /* 5 --- 9 */ 30 , 46 , 62 , 94 , 126 , /* 10 --- 14 */ 190 , 254 , 382 , 510 , 766 , /* 15 --- 19 */ 1022 , 1534 , 2046 , 3070 , 4094 , /* 20 --- 24 */ 6142 , 8190 , 12286 , 16382 , 24574 , /* 25 --- 29 */ 32766 , 49150 , 65534 , 98302 , 131070 , /* 30 --- 34 */ 196606 , 262142 , 393214 , 524286 , 655358 , /* 35 --- 39 */ 786430 , 917502 , 1048574, 1179646, 1310718, /* 40 --- 44 */ 1441790, 1572862, 1703934, 1835006, 1966078, /* 45 --- 49 */ 2097150 /* extra */ }; /* Mapping: offset slot => how many extra bits must be read and added to the * corresponding offset slot base to decode the match offset. */ const u8 lzx_extra_offset_bits[LZX_MAX_OFFSET_SLOTS] = { 0 , 0 , 0 , 0 , 1 , 1 , 2 , 2 , 3 , 3 , 4 , 4 , 5 , 5 , 6 , 6 , 7 , 7 , 8 , 8 , 9 , 9 , 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, }; /* Round the specified buffer size up to the next valid LZX window size, and * return its order (log2). Or, if the buffer size is 0 or greater than the * largest valid LZX window size, return 0. */ unsigned lzx_get_window_order(size_t max_bufsize) { if (max_bufsize == 0 || max_bufsize > LZX_MAX_WINDOW_SIZE) return 0; return max(ilog2_ceil(max_bufsize), LZX_MIN_WINDOW_ORDER); } /* Given a valid LZX window order, return the number of symbols that will exist * in the main Huffman code. */ unsigned lzx_get_num_main_syms(unsigned window_order) { /* Note: one would expect that the maximum match offset would be * 'window_size - LZX_MIN_MATCH_LEN', which would occur if the first two * bytes were to match the last two bytes. However, the format * disallows this case. This reduces the number of needed offset slots * by 1. */ u32 window_size = (u32)1 << window_order; u32 max_offset = window_size - LZX_MIN_MATCH_LEN - 1; unsigned num_offset_slots = 30; while (max_offset >= lzx_offset_slot_base[num_offset_slots]) num_offset_slots++; return LZX_NUM_CHARS + (num_offset_slots * LZX_NUM_LEN_HEADERS); } static void do_translate_target(void *target, s32 input_pos) { s32 abs_offset, rel_offset; rel_offset = get_unaligned_le32(target); if (rel_offset >= -input_pos && rel_offset < LZX_WIM_MAGIC_FILESIZE) { if (rel_offset < LZX_WIM_MAGIC_FILESIZE - input_pos) { /* "good translation" */ abs_offset = rel_offset + input_pos; } else { /* "compensating translation" */ abs_offset = rel_offset - LZX_WIM_MAGIC_FILESIZE; } put_unaligned_le32(abs_offset, target); } } static void undo_translate_target(void *target, s32 input_pos) { s32 abs_offset, rel_offset; abs_offset = get_unaligned_le32(target); if (abs_offset >= 0) { if (abs_offset < LZX_WIM_MAGIC_FILESIZE) { /* "good translation" */ rel_offset = abs_offset - input_pos; put_unaligned_le32(rel_offset, target); } } else { if (abs_offset >= -input_pos) { /* "compensating translation" */ rel_offset = abs_offset + LZX_WIM_MAGIC_FILESIZE; put_unaligned_le32(rel_offset, target); } } } /* * Do or undo the 'E8' preprocessing used in LZX. Before compression, the * uncompressed data is preprocessed by changing the targets of x86 CALL * instructions from relative offsets to absolute offsets. After decompression, * the translation is undone by changing the targets of x86 CALL instructions * from absolute offsets to relative offsets. * * Note that despite its intent, E8 preprocessing can be done on any data even * if it is not actually x86 machine code. In fact, E8 preprocessing appears to * always be used in LZX-compressed resources in WIM files; there is no bit to * indicate whether it is used or not, unlike in the LZX compressed format as * used in cabinet files, where a bit is reserved for that purpose. * * E8 preprocessing is disabled in the last 6 bytes of the uncompressed data, * which really means the 5-byte call instruction cannot start in the last 10 * bytes of the uncompressed data. This is one of the errors in the LZX * documentation. * * E8 preprocessing does not appear to be disabled after the 32768th chunk of a * WIM resource, which apparently is another difference from the LZX compression * used in cabinet files. * * E8 processing is supposed to take the file size as a parameter, as it is used * in calculating the translated jump targets. But in WIM files, this file size * is always the same (LZX_WIM_MAGIC_FILESIZE == 12000000). */ static void lzx_e8_filter(u8 *data, u32 size, void (*process_target)(void *, s32)) { #if !defined(__SSE2__) && !defined(__AVX2__) /* * A worthwhile optimization is to push the end-of-buffer check into the * relatively rare E8 case. This is possible if we replace the last six * bytes of data with E8 bytes; then we are guaranteed to hit an E8 byte * before reaching end-of-buffer. In addition, this scheme guarantees * that no translation can begin following an E8 byte in the last 10 * bytes because a 4-byte offset containing E8 as its high byte is a * large negative number that is not valid for translation. That is * exactly what we need. */ u8 *tail; u8 saved_bytes[6]; u8 *p; if (size <= 10) return; tail = &data[size - 6]; memcpy(saved_bytes, tail, 6); memset(tail, 0xE8, 6); p = data; for (;;) { while (*p != 0xE8) p++; if (p >= tail) break; (*process_target)(p + 1, p - data); p += 5; } memcpy(tail, saved_bytes, 6); #else /* SSE2 or AVX-2 optimized version for x86_64 */ u8 *p = data; u64 valid_mask = ~0; if (size <= 10) return; #ifdef __AVX2__ # define ALIGNMENT_REQUIRED 32 #else # define ALIGNMENT_REQUIRED 16 #endif /* Process one byte at a time until the pointer is properly aligned. */ while ((uintptr_t)p % ALIGNMENT_REQUIRED != 0) { if (p >= data + size - 10) return; if (*p == 0xE8 && (valid_mask & 1)) { (*process_target)(p + 1, p - data); valid_mask &= ~0x1F; } p++; valid_mask >>= 1; valid_mask |= (u64)1 << 63; } if (data + size - p >= 64) { /* Vectorized processing */ /* Note: we use a "trap" E8 byte to eliminate the need to check * for end-of-buffer in the inner loop. This byte is carefully * positioned so that it will never be changed by a previous * translation before it is detected. */ u8 *trap = p + ((data + size - p) & ~31) - 32 + 4; u8 saved_byte = *trap; *trap = 0xE8; for (;;) { u32 e8_mask; u8 *orig_p = p; #ifdef __AVX2__ const __m256i e8_bytes = _mm256_set1_epi8(0xE8); for (;;) { __m256i bytes = *(const __m256i *)p; __m256i cmpresult = _mm256_cmpeq_epi8(bytes, e8_bytes); e8_mask = _mm256_movemask_epi8(cmpresult); if (e8_mask) break; p += 32; } #else const __m128i e8_bytes = _mm_set1_epi8(0xE8); for (;;) { /* Read the next 32 bytes of data and test them * for E8 bytes. */ __m128i bytes1 = *(const __m128i *)p; __m128i bytes2 = *(const __m128i *)(p + 16); __m128i cmpresult1 = _mm_cmpeq_epi8(bytes1, e8_bytes); __m128i cmpresult2 = _mm_cmpeq_epi8(bytes2, e8_bytes); u32 mask1 = _mm_movemask_epi8(cmpresult1); u32 mask2 = _mm_movemask_epi8(cmpresult2); /* The masks have a bit set for each E8 byte. * We stay in this fast inner loop as long as * there are no E8 bytes. */ if (mask1 | mask2) { e8_mask = mask1 | (mask2 << 16); break; } p += 32; } #endif /* Did we pass over data with no E8 bytes? */ if (p != orig_p) valid_mask = ~0; /* Are we nearing end-of-buffer? */ if (p == trap - 4) break; /* Process the E8 bytes. However, the AND with * 'valid_mask' ensures we never process an E8 byte that * was itself part of a translation target. */ while ((e8_mask &= valid_mask)) { unsigned bit = bsf32(e8_mask); (*process_target)(p + bit + 1, p + bit - data); valid_mask &= ~((u64)0x1F << bit); } valid_mask >>= 32; valid_mask |= 0xFFFFFFFF00000000; p += 32; } *trap = saved_byte; } /* Approaching the end of the buffer; process one byte a time. */ while (p < data + size - 10) { if (*p == 0xE8 && (valid_mask & 1)) { (*process_target)(p + 1, p - data); valid_mask &= ~0x1F; } p++; valid_mask >>= 1; valid_mask |= (u64)1 << 63; } #endif /* __SSE2__ || __AVX2__ */ } void lzx_preprocess(u8 *data, u32 size) { lzx_e8_filter(data, size, do_translate_target); } void lzx_postprocess(u8 *data, u32 size) { lzx_e8_filter(data, size, undo_translate_target); } wimlib-1.13.1/src/registry.c0000644000175000017500000004645413160354225012622 00000000000000/* * registry.c * * Extract information from Windows NT registry hives. */ /* * Copyright (C) 2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include "wimlib/encoding.h" #include "wimlib/endianness.h" #include "wimlib/error.h" #include "wimlib/registry.h" #include "wimlib/util.h" /* Registry hive file header */ struct regf { #define REGF_MAGIC cpu_to_le32(0x66676572) /* "regf" */ le32 magic; le32 f1[4]; #define REGF_MAJOR cpu_to_le32(1) le32 major_version; le32 minor_version; le32 f2[2]; le32 root_key_offset; /* Offset, in hbin area, to root nk */ le32 total_hbin_size; /* Total size of all hbins */ le32 f3[1013]; u8 hbin_area[0]; /* Start of hbin area */ } _packed_attribute; /* Cell header */ struct cell { /* The cell size in bytes, negated for in-use cells */ le32 size; /* Magic characters which identify the cell type */ le16 magic; } _packed_attribute; /* NK cell - represents a registry key */ struct nk { #define NK_MAGIC cpu_to_le16(0x6B6E) /* "nk" */ struct cell base; #define NK_COMPRESSED_NAME cpu_to_le16(0x0020) le16 flags; le64 unknown_0x08; le32 unknown_0x10; le32 parent_offset; le32 num_subkeys; le32 unknown_0x1C; le32 subkey_list_offset; le32 unknown_0x24; le32 num_values; le32 value_list_offset; le32 unknown_0x30; le32 unknown_0x34; le16 unknown_0x38; le16 unknown_0x3A; le32 unknown_0x3C; le32 unknown_0x40; le32 unknown_0x44; le32 unknown_0x48; le16 name_size; le16 unknown_0x4E; char name[0]; } _packed_attribute; /* Subkey list cell. There are four types. LF, LH, and LI cells reference * subkey NK cells directly, while RI cells reference other subkey lists. All * contain a count followed by that many 32-bit offsets. But LF and LH cells * contain a 32-bit hash along with each offset, while LI and RI cells only * contain offsets. */ struct subkey_list { #define LF_MAGIC cpu_to_le16(0x666C) /* "lf" */ #define LH_MAGIC cpu_to_le16(0x686C) /* "lh" */ #define LI_MAGIC cpu_to_le16(0x696C) /* "li" */ #define RI_MAGIC cpu_to_le16(0x6972) /* "ri" */ struct cell base; le16 num_offsets; le32 elements[0]; } _packed_attribute; /* Value list cell - contains a list of value references */ struct value_list { le32 size; le32 vk_offsets[0]; } _packed_attribute; /* VK cell - contains a value's data, or a reference to it */ struct vk { #define VK_MAGIC cpu_to_le16(0x6B76) struct cell base; le16 name_size; le32 data_size; le32 data_offset; #define REG_NONE cpu_to_le32(0) #define REG_SZ cpu_to_le32(1) #define REG_EXPAND_SZ cpu_to_le32(2) #define REG_BINARY cpu_to_le32(3) #define REG_DWORD cpu_to_le32(4) #define REG_DWORD_LITTLE_ENDIAN cpu_to_le32(4) #define REG_DWORD_BIG_ENDIAN cpu_to_le32(5) #define REG_LINK cpu_to_le32(6) #define REG_MULTI_SZ cpu_to_le32(7) #define REG_RESOURCE_LIST cpu_to_le32(8) #define REG_FULL_RESOURCE_DESCRIPTOR cpu_to_le32(9) #define REG_RESOURCE_REQUIREMENTS_LIST cpu_to_le32(10) #define REG_QWORD cpu_to_le32(11) #define REG_QWORD_LITTLE_ENDIAN cpu_to_le32(11) le32 data_type; #define VK_COMPRESSED_NAME cpu_to_le16(0x0001) le16 flags; le16 unknown_0x16; char name[0]; }; /* Data cell - contains a value's data */ struct data_cell { le32 size; u8 data[0]; }; /* Arbitrary limits for safety */ #define MAX_VALUES 65536 #define MAX_VALUE_SIZE 1048576 #define MAX_SUBKEYS 65536 #define MAX_SUBKEY_LIST_LEVELS 5 #define MAX_SUBKEY_LISTS 4096 static enum hive_status translate_wimlib_error(int ret) { if (likely(!ret)) return HIVE_OK; if (ret == WIMLIB_ERR_NOMEM) return HIVE_OUT_OF_MEMORY; return HIVE_UNSUPPORTED; } /* Compare a UTF-16LE name with a key or value name in the registry. The * comparison is case insensitive. */ static inline bool names_equal(const utf16lechar *name, size_t name_nchars, const void *disk_name, size_t disk_name_size, bool compressed) { if (compressed) { /* ISO-8859-1 (LATIN1) on-disk */ const u8 *p = disk_name; if (disk_name_size != name_nchars) return false; for (size_t i = 0; i < name_nchars; i++) if (upcase[le16_to_cpu(name[i])] != upcase[p[i]]) return false; return true; } else { /* UTF-16LE on disk */ disk_name_size /= 2; if (disk_name_size != name_nchars) return false; return !cmp_utf16le_strings(name, name_nchars, disk_name, disk_name_size, true); } } /* Get a pointer to a cell, with alignment and bounds checking. Returns NULL if * the requested information does not specify a properly aligned, sized, and * in-use cell. */ static const void * get_cell_pointer(const struct regf *regf, le32 offset, size_t wanted_size) { u32 total = le32_to_cpu(regf->total_hbin_size); u32 offs = le32_to_cpu(offset); const struct cell *cell; u32 actual_size; if ((offs > total) || (offs & 7) || (wanted_size > total - offs)) return NULL; cell = (const struct cell *)®f->hbin_area[offs]; actual_size = -le32_to_cpu(cell->size); if (actual_size > INT32_MAX) /* Cell unused, or size was INT32_MIN? */ return NULL; if (wanted_size > actual_size) /* Cell too small? */ return NULL; return cell; } /* Revalidate the cell with its full length. Returns true iff the cell is * valid. */ static bool revalidate_cell(const struct regf *regf, le32 offset, size_t wanted_size) { return get_cell_pointer(regf, offset, wanted_size) != NULL; } struct subkey_iteration_stats { /* The number of additional levels of descendent subkey lists that may * be visited (currently, i.e. at this point in the iteration) before * our safety limit of MAX_SUBKEY_LIST_LEVELS is reached */ u32 levels_remaining; /* The number of additional subkey lists that may be visited until our * safety limit of MAX_SUBKEY_LISTS is reached */ u32 subkey_lists_remaining; /* The number of subkeys remaining to be found. Since the number of * subkeys is known from the parent nk cell, this should be 0 at the end * of the iteration. */ u32 subkeys_remaining; }; typedef enum hive_status (*subkey_cb_t)(const struct nk *, void *); static enum hive_status iterate_subkeys_recursive(const struct regf *regf, le32 subkey_list_offset, subkey_cb_t cb, void *cb_ctx, struct subkey_iteration_stats *stats) { const struct subkey_list *list; unsigned num_offsets; size_t extra_size; unsigned increment; const le32 *p; enum hive_status status; if (stats->levels_remaining == 0 || stats->subkey_lists_remaining == 0) return HIVE_CORRUPT; stats->subkey_lists_remaining--; list = get_cell_pointer(regf, subkey_list_offset, sizeof(struct subkey_list)); if (!list) return HIVE_CORRUPT; num_offsets = le16_to_cpu(list->num_offsets); extra_size = num_offsets * sizeof(list->elements[0]); increment = 1; if (list->base.magic == LF_MAGIC || list->base.magic == LH_MAGIC) { /* Hashes are included */ extra_size *= 2; increment = 2; } if (!revalidate_cell(regf, subkey_list_offset, sizeof(struct subkey_list) + extra_size)) return HIVE_CORRUPT; p = list->elements; switch (list->base.magic) { case LF_MAGIC: case LH_MAGIC: case LI_MAGIC: /* Children are subkeys */ if (stats->subkeys_remaining < num_offsets) return HIVE_CORRUPT; stats->subkeys_remaining -= num_offsets; while (num_offsets--) { const struct nk *sub_nk; sub_nk = get_cell_pointer(regf, *p, sizeof(struct nk)); if (!sub_nk || sub_nk->base.magic != NK_MAGIC) return HIVE_CORRUPT; if (!revalidate_cell(regf, *p, sizeof(struct nk) + le16_to_cpu(sub_nk->name_size))) return HIVE_CORRUPT; status = (*cb)(sub_nk, cb_ctx); if (status != HIVE_OK) return status; p += increment; } return HIVE_OK; case RI_MAGIC: /* Children are subkey lists */ status = HIVE_OK; stats->levels_remaining--; while (num_offsets--) { status = iterate_subkeys_recursive(regf, *p++, cb, cb_ctx, stats); if (status != HIVE_OK) break; } stats->levels_remaining++; return status; default: return HIVE_UNSUPPORTED; } } /* Call @cb on each subkey cell of the key @nk. */ static enum hive_status iterate_subkeys(const struct regf *regf, const struct nk *nk, subkey_cb_t cb, void *cb_ctx) { u32 num_subkeys = le32_to_cpu(nk->num_subkeys); struct subkey_iteration_stats stats; enum hive_status status; if (num_subkeys == 0) return HIVE_OK; if (num_subkeys > MAX_SUBKEYS) return HIVE_CORRUPT; stats.levels_remaining = MAX_SUBKEY_LIST_LEVELS; stats.subkey_lists_remaining = MAX_SUBKEY_LISTS; stats.subkeys_remaining = num_subkeys; status = iterate_subkeys_recursive(regf, nk->subkey_list_offset, cb, cb_ctx, &stats); if (stats.subkeys_remaining != 0 && status == HIVE_OK) status = HIVE_CORRUPT; return status; } struct lookup_subkey_ctx { const utf16lechar *key_name; size_t key_name_nchars; const struct nk *result; }; static enum hive_status lookup_subkey_cb(const struct nk *sub_nk, void *_ctx) { struct lookup_subkey_ctx *ctx = _ctx; if (names_equal(ctx->key_name, ctx->key_name_nchars, sub_nk->name, le16_to_cpu(sub_nk->name_size), (sub_nk->flags & NK_COMPRESSED_NAME) != 0)) { ctx->result = sub_nk; return HIVE_ITERATION_STOPPED; } return HIVE_OK; } /* * Given a registry key cell @nk, look up the next component of the key * *key_namep. If found, return HIVE_OK, advance *key_namep past the key name * component, and return the subkey cell in @sub_nk_ret. Otherwise, return * another HIVE_* error code. */ static enum hive_status lookup_subkey(const struct regf *regf, const utf16lechar **key_namep, const struct nk *nk, const struct nk **sub_nk_ret) { const utf16lechar *key_name = *key_namep; size_t key_name_nchars = 0; struct lookup_subkey_ctx ctx; enum hive_status status; while (key_name[key_name_nchars] != cpu_to_le16('\0') && key_name[key_name_nchars] != cpu_to_le16('\\')) key_name_nchars++; ctx.key_name = key_name; ctx.key_name_nchars = key_name_nchars; ctx.result = NULL; status = iterate_subkeys(regf, nk, lookup_subkey_cb, &ctx); if (!ctx.result) { if (status == HIVE_OK) status = HIVE_KEY_NOT_FOUND; return status; } key_name += key_name_nchars; while (*key_name == cpu_to_le16('\\')) key_name++; *key_namep = key_name; *sub_nk_ret = ctx.result; return HIVE_OK; } /* Find the nk cell for the key named @key_name in the registry hive @regf. */ static enum hive_status lookup_key(const struct regf *regf, const tchar *key_name, const struct nk **nk_ret) { const struct nk *nk; enum hive_status status; const utf16lechar *key_uname, *key_unamep; nk = get_cell_pointer(regf, regf->root_key_offset, sizeof(struct nk)); if (!nk || nk->base.magic != NK_MAGIC) return HIVE_CORRUPT; status = translate_wimlib_error(tstr_get_utf16le(key_name, &key_uname)); if (status != HIVE_OK) return status; key_unamep = key_uname; while (*key_unamep) { status = lookup_subkey(regf, &key_unamep, nk, &nk); if (status != HIVE_OK) goto out; } *nk_ret = nk; status = HIVE_OK; out: tstr_put_utf16le(key_uname); return status; } /* Find the vk cell for the value named @value_name of the key named @key_name * in the registry hive @regf. */ static enum hive_status lookup_value(const struct regf *regf, const tchar *key_name, const tchar *value_name, const struct vk **vk_ret) { enum hive_status status; const struct nk *nk; size_t num_values; const struct value_list *value_list; const utf16lechar *value_uname; size_t value_uname_nchars; /* Look up the nk cell for the key. */ status = lookup_key(regf, key_name, &nk); if (status != HIVE_OK) return status; num_values = le32_to_cpu(nk->num_values); if (num_values == 0) /* No values? */ return HIVE_VALUE_NOT_FOUND; if (num_values > MAX_VALUES) return HIVE_CORRUPT; value_list = get_cell_pointer(regf, nk->value_list_offset, sizeof(struct value_list) + (num_values * sizeof(value_list->vk_offsets[0]))); if (!value_list) return HIVE_CORRUPT; /* Look for the value in the value list. */ status = translate_wimlib_error( tstr_get_utf16le_and_len(value_name, &value_uname, &value_uname_nchars)); if (status != HIVE_OK) return status; value_uname_nchars /= 2; for (size_t i = 0; i < num_values; i++) { const struct vk *vk; size_t name_size; status = HIVE_CORRUPT; vk = get_cell_pointer(regf, value_list->vk_offsets[i], sizeof(struct vk)); if (!vk || vk->base.magic != VK_MAGIC) goto out; name_size = le16_to_cpu(vk->name_size); if (!revalidate_cell(regf, value_list->vk_offsets[i], sizeof(struct vk) + name_size)) goto out; if (names_equal(value_uname, value_uname_nchars, vk->name, name_size, (vk->flags & VK_COMPRESSED_NAME) != 0)) { *vk_ret = vk; status = HIVE_OK; goto out; } } status = HIVE_VALUE_NOT_FOUND; out: tstr_put_utf16le(value_uname); return status; } /* * Retrieve the data of the value named @value_name of the key named @key_name * in the registry hive @regf. If the value was found, return HIVE_OK and * return the data, its size, and its type in @data_ret, @data_size_ret, and * @data_type_ret. Otherwise, return another HIVE_* error code. */ static enum hive_status retrieve_value(const struct regf *regf, const tchar *key_name, const tchar *value_name, void **data_ret, size_t *data_size_ret, le32 *data_type_ret) { enum hive_status status; const struct vk *vk; size_t data_size; bool is_inline; const void *data; /* Find the vk cell. */ status = lookup_value(regf, key_name, value_name, &vk); if (status != HIVE_OK) return status; /* Extract the value data from the vk cell (for inline data) or from the * data cell which it references (for non-inline data). */ data_size = le32_to_cpu(vk->data_size); is_inline = (data_size & 0x80000000); data_size &= 0x7FFFFFFF; if (data_size > MAX_VALUE_SIZE) return HIVE_CORRUPT; if (is_inline) { if (data_size > 4) return HIVE_CORRUPT; data = &vk->data_offset; } else { const struct data_cell *data_cell; data_cell = get_cell_pointer(regf, vk->data_offset, sizeof(struct data_cell)); if (!data_cell) return HIVE_CORRUPT; if (!revalidate_cell(regf, vk->data_offset, sizeof(struct data_cell) + data_size)) return HIVE_UNSUPPORTED; /* Possibly a big data cell */ data = data_cell->data; } *data_ret = memdup(data, data_size); if (!*data_ret) return HIVE_OUT_OF_MEMORY; *data_size_ret = data_size; *data_type_ret = vk->data_type; return HIVE_OK; } /* Validate the registry hive file given in memory as @hive_mem and @hive_size. * If valid, return HIVE_OK. If invalid, return another HIVE_* error code. */ enum hive_status hive_validate(const void *hive_mem, size_t hive_size) { const struct regf *regf = hive_mem; STATIC_ASSERT(sizeof(struct regf) == 4096); if (hive_size < sizeof(struct regf)) return HIVE_CORRUPT; if (regf->magic != REGF_MAGIC || regf->major_version != REGF_MAJOR) return HIVE_UNSUPPORTED; if (le32_to_cpu(regf->total_hbin_size) > hive_size - sizeof(struct regf)) return HIVE_CORRUPT; return HIVE_OK; } /* Get a string value from the registry hive file. */ enum hive_status hive_get_string(const struct regf *regf, const tchar *key_name, const tchar *value_name, tchar **value_ret) { void *data; size_t data_size; le32 data_type; enum hive_status status; /* Retrieve the raw value data. */ status = retrieve_value(regf, key_name, value_name, &data, &data_size, &data_type); if (status != HIVE_OK) return status; /* Interpret the data as a string, when possible. */ switch (data_type) { case REG_SZ: case REG_MULTI_SZ: status = translate_wimlib_error( utf16le_to_tstr(data, data_size, value_ret, &data_size)); break; default: status = HIVE_VALUE_IS_WRONG_TYPE; break; } FREE(data); return status; } /* Get a number value from the registry hive file. */ enum hive_status hive_get_number(const struct regf *regf, const tchar *key_name, const tchar *value_name, s64 *value_ret) { void *data; size_t data_size; le32 data_type; enum hive_status status; /* Retrieve the raw value data. */ status = retrieve_value(regf, key_name, value_name, &data, &data_size, &data_type); if (status != HIVE_OK) return status; /* Interpret the data as a number, when possible. */ switch (data_type) { case REG_DWORD_LITTLE_ENDIAN: if (data_size == 4) { *value_ret = le32_to_cpu(*(le32 *)data); status = HIVE_OK; } else { status = HIVE_CORRUPT; } break; case REG_DWORD_BIG_ENDIAN: if (data_size == 4) { *value_ret = be32_to_cpu(*(be32 *)data); status = HIVE_OK; } else { status = HIVE_CORRUPT; } break; case REG_QWORD_LITTLE_ENDIAN: if (data_size == 8) { *value_ret = le64_to_cpu(*(le64 *)data); status = HIVE_OK; } else { status = HIVE_CORRUPT; } break; default: status = HIVE_VALUE_IS_WRONG_TYPE; break; } FREE(data); return status; } static enum hive_status append_subkey_name(const struct nk *sub_nk, void *_next_subkey_p) { size_t name_size = le16_to_cpu(sub_nk->name_size); tchar *subkey; tchar ***next_subkeyp = _next_subkey_p; if (sub_nk->flags & NK_COMPRESSED_NAME) { subkey = MALLOC((name_size + 1) * sizeof(tchar)); if (!subkey) return HIVE_OUT_OF_MEMORY; for (size_t i = 0; i < name_size; i++) subkey[i] = sub_nk->name[i]; subkey[name_size] = '\0'; } else { enum hive_status status; status = translate_wimlib_error( utf16le_to_tstr((utf16lechar *)sub_nk->name, name_size, &subkey, NULL)); if (status != HIVE_OK) return status; } **next_subkeyp = subkey; ++*next_subkeyp; return HIVE_OK; } /* List the subkeys of the specified registry key. */ enum hive_status hive_list_subkeys(const struct regf *regf, const tchar *key_name, tchar ***subkeys_ret) { enum hive_status status; const struct nk *nk; tchar **subkeys; tchar **next_subkey; status = lookup_key(regf, key_name, &nk); if (status != HIVE_OK) return status; if (le32_to_cpu(nk->num_subkeys) > MAX_SUBKEYS) return HIVE_CORRUPT; subkeys = CALLOC(le32_to_cpu(nk->num_subkeys) + 1, sizeof(subkeys[0])); if (!subkeys) return HIVE_OUT_OF_MEMORY; next_subkey = subkeys; status = iterate_subkeys(regf, nk, append_subkey_name, &next_subkey); if (status == HIVE_OK) *subkeys_ret = subkeys; else hive_free_subkeys_list(subkeys); return status; } void hive_free_subkeys_list(tchar **subkeys) { for (tchar **p = subkeys; *p; p++) FREE(*p); FREE(subkeys); } const char * hive_status_to_string(enum hive_status status) { switch (status) { case HIVE_OK: return "HIVE_OK"; case HIVE_CORRUPT: return "HIVE_CORRUPT"; case HIVE_UNSUPPORTED: return "HIVE_UNSUPPORTED"; case HIVE_KEY_NOT_FOUND: return "HIVE_KEY_NOT_FOUND"; case HIVE_VALUE_NOT_FOUND: return "HIVE_VALUE_NOT_FOUND"; case HIVE_VALUE_IS_WRONG_TYPE: return "HIVE_VALUE_IS_WRONG_TYPE"; case HIVE_OUT_OF_MEMORY: return "HIVE_OUT_OF_MEMORY"; case HIVE_ITERATION_STOPPED: return "HIVE_ITERATION_STOPPED"; } return NULL; } wimlib-1.13.1/src/util.c0000644000175000017500000001460613160354225011721 00000000000000/* * util.c - utility functions */ /* * Copyright (C) 2012-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #ifdef HAVE_SYS_SYSCTL_H # include # include #endif #ifdef HAVE_SYS_SYSCALL_H # include #endif #include #include "wimlib.h" #include "wimlib/assert.h" #include "wimlib/error.h" #include "wimlib/timestamp.h" #include "wimlib/util.h" #include "wimlib/xml.h" /******************* * Memory allocation *******************/ static void *(*wimlib_malloc_func) (size_t) = malloc; static void (*wimlib_free_func) (void *) = free; static void *(*wimlib_realloc_func)(void *, size_t) = realloc; void * wimlib_malloc(size_t size) { void *ptr; retry: ptr = (*wimlib_malloc_func)(size); if (unlikely(!ptr)) { if (size == 0) { size = 1; goto retry; } } return ptr; } void wimlib_free_memory(void *ptr) { (*wimlib_free_func)(ptr); } void * wimlib_realloc(void *ptr, size_t size) { if (size == 0) size = 1; return (*wimlib_realloc_func)(ptr, size); } void * wimlib_calloc(size_t nmemb, size_t size) { size_t total_size = nmemb * size; void *p; if (size != 0 && nmemb > SIZE_MAX / size) { errno = ENOMEM; return NULL; } p = MALLOC(total_size); if (p) p = memset(p, 0, total_size); return p; } char * wimlib_strdup(const char *str) { return memdup(str, strlen(str) + 1); } #ifdef __WIN32__ wchar_t * wimlib_wcsdup(const wchar_t *str) { return memdup(str, (wcslen(str) + 1) * sizeof(wchar_t)); } #endif void * wimlib_aligned_malloc(size_t size, size_t alignment) { wimlib_assert(is_power_of_2(alignment)); void *ptr = MALLOC(sizeof(void *) + alignment - 1 + size); if (ptr) { void *orig_ptr = ptr; ptr = (void *)ALIGN((uintptr_t)ptr + sizeof(void *), alignment); ((void **)ptr)[-1] = orig_ptr; } return ptr; } void wimlib_aligned_free(void *ptr) { if (ptr) FREE(((void **)ptr)[-1]); } void * memdup(const void *mem, size_t size) { void *ptr = MALLOC(size); if (ptr) ptr = memcpy(ptr, mem, size); return ptr; } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_set_memory_allocator(void *(*malloc_func)(size_t), void (*free_func)(void *), void *(*realloc_func)(void *, size_t)) { wimlib_malloc_func = malloc_func ? malloc_func : malloc; wimlib_free_func = free_func ? free_func : free; wimlib_realloc_func = realloc_func ? realloc_func : realloc; xml_set_memory_allocator(wimlib_malloc_func, wimlib_free_func, wimlib_realloc_func); return 0; } /******************* * String utilities *******************/ #ifndef HAVE_MEMPCPY void *mempcpy(void *dst, const void *src, size_t n) { return memcpy(dst, src, n) + n; } #endif /************************** * Random number generation **************************/ #ifndef __WIN32__ /* * Generate @n cryptographically secure random bytes (thread-safe) * * This is the UNIX version. It uses the Linux getrandom() system call if * available; otherwise, it falls back to reading from /dev/urandom. */ void get_random_bytes(void *p, size_t n) { if (n == 0) return; #ifdef HAVE_NR_GETRANDOM static bool getrandom_unavailable; if (getrandom_unavailable) goto try_dev_urandom; do { int res = syscall(__NR_getrandom, p, n, 0); if (unlikely(res < 0)) { if (errno == ENOSYS) { getrandom_unavailable = true; goto try_dev_urandom; } if (errno == EINTR) continue; ERROR_WITH_ERRNO("getrandom() failed"); wimlib_assert(0); res = 0; } p += res; n -= res; } while (n != 0); return; try_dev_urandom: ; #endif /* HAVE_NR_GETRANDOM */ int fd = open("/dev/urandom", O_RDONLY); if (fd < 0) { ERROR_WITH_ERRNO("Unable to open /dev/urandom"); wimlib_assert(0); } do { int res = read(fd, p, min(n, INT_MAX)); if (unlikely(res < 0)) { if (errno == EINTR) continue; ERROR_WITH_ERRNO("Error reading from /dev/urandom"); wimlib_assert(0); res = 0; } p += res; n -= res; } while (n != 0); close(fd); } #endif /* !__WIN32__ */ /* * Generate @n cryptographically secure random alphanumeric characters * (thread-safe) * * This is implemented on top of get_random_bytes(). For efficiency the calls * to get_random_bytes() are batched. */ void get_random_alnum_chars(tchar *p, size_t n) { u32 r[64]; int r_idx = 0; int r_end = 0; for (; n != 0; p++, n--) { tchar x; if (r_idx >= r_end) { r_idx = 0; r_end = min(n, ARRAY_LEN(r)); get_random_bytes(r, r_end * sizeof(r[0])); } STATIC_ASSERT(sizeof(r[0]) == sizeof(u32)); while (unlikely(r[r_idx] >= UINT32_MAX - (UINT32_MAX % 62))) get_random_bytes(&r[r_idx], sizeof(r[0])); x = r[r_idx++] % 62; if (x < 26) *p = 'a' + x; else if (x < 52) *p = 'A' + x - 26; else *p = '0' + x - 52; } } /************************ * System information ************************/ #ifndef __WIN32__ unsigned get_available_cpus(void) { long n = sysconf(_SC_NPROCESSORS_ONLN); if (n < 1 || n >= UINT_MAX) { WARNING("Failed to determine number of processors; assuming 1."); return 1; } return n; } #endif /* !__WIN32__ */ #ifndef __WIN32__ u64 get_available_memory(void) { #if defined(_SC_PAGESIZE) && defined(_SC_PHYS_PAGES) long page_size = sysconf(_SC_PAGESIZE); long num_pages = sysconf(_SC_PHYS_PAGES); if (page_size <= 0 || num_pages <= 0) goto default_size; return ((u64)page_size * (u64)num_pages); #else int mib[2] = {CTL_HW, HW_MEMSIZE}; u64 memsize; size_t len = sizeof(memsize); if (sysctl(mib, ARRAY_LEN(mib), &memsize, &len, NULL, 0) < 0 || len != 8) goto default_size; return memsize; #endif default_size: WARNING("Failed to determine available memory; assuming 1 GiB"); return (u64)1 << 30; } #endif /* !__WIN32__ */ wimlib-1.13.1/src/paths.c0000644000175000017500000000706313160354225012062 00000000000000/* * paths.c - Path manipulation routines */ /* * Copyright (C) 2012, 2013 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include "wimlib.h" #include "wimlib/paths.h" #include "wimlib/util.h" /* Like the basename() function, but does not modify @path; it just returns a * pointer to it. This assumes the path separator is the * OS_PREFERRED_PATH_SEPARATOR. */ const tchar * path_basename(const tchar *path) { return path_basename_with_len(path, tstrlen(path)); } /* Like path_basename(), but take an explicit string length. */ const tchar * path_basename_with_len(const tchar *path, size_t len) { const tchar *p = &path[len]; do { if (p == path) return &path[len]; } while (*--p == OS_PREFERRED_PATH_SEPARATOR); do { if (p == path) return &path[0]; } while (*--p != OS_PREFERRED_PATH_SEPARATOR); return ++p; } /* Returns a pointer to the part of @path following the first colon in the last * path component, or NULL if the last path component does not contain a colon * or has no characters following the first colon. */ const tchar * path_stream_name(const tchar *path) { const tchar *base = path_basename(path); const tchar *stream_name = tstrchr(base, T(':')); if (stream_name == NULL || *(stream_name + 1) == T('\0')) return NULL; else return stream_name + 1; } /* Collapse and translate path separators, and strip trailing slashes. Doesn't * add or delete a leading slash. * * @in may alias @out. */ void do_canonicalize_path(const tchar *in, tchar *out) { tchar *orig_out = out; while (*in) { if (is_any_path_separator(*in)) { /* Collapse multiple path separators into one */ *out++ = WIM_PATH_SEPARATOR; do { in++; } while (is_any_path_separator(*in)); } else { /* Copy non-path-separator character */ *out++ = *in++; } } /* Remove trailing slash if existent */ if (out - orig_out > 1 && *(out - 1) == WIM_PATH_SEPARATOR) --out; *out = T('\0'); } /* * canonicalize_wim_path() - Given a user-provided path to a file within a WIM * image, translate it into a "canonical" path. * * - Translate both types of slash into a consistent type (WIM_PATH_SEPARATOR). * - Collapse path separators. * - Add leading slash if missing. * - Strip trailing slashes. * * Examples (with WIM_PATH_SEPARATOR == '/'): * * => / [ either NULL or empty string ] * / => / * \ => / * hello => /hello * \hello => /hello * \hello => /hello * /hello/ => /hello * \hello/ => /hello * /hello//1 => /hello/1 * \\hello\\1\\ => /hello/1 */ tchar * canonicalize_wim_path(const tchar *wim_path) { const tchar *in; tchar *out; tchar *result; in = wim_path; if (!in) in = T(""); result = MALLOC((1 + tstrlen(in) + 1) * sizeof(result[0])); if (!result) return NULL; out = result; /* Add leading slash if missing */ if (!is_any_path_separator(*in)) *out++ = WIM_PATH_SEPARATOR; do_canonicalize_path(in, out); return result; } wimlib-1.13.1/src/inode_fixup.c0000644000175000017500000001451713160354224013255 00000000000000/* * inode_fixup.c */ /* * Copyright (C) 2012, 2013, 2014 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib/dentry.h" #include "wimlib/error.h" #include "wimlib/inode.h" #include "wimlib/inode_table.h" struct inode_fixup_params { struct wim_inode_table inode_table; unsigned long num_dir_hard_links; unsigned long num_inconsistent_inodes; }; #define MAX_DIR_HARD_LINK_WARNINGS 8 static bool inodes_consistent(const struct wim_inode *inode_1, const struct wim_inode *inode_2) { /* This certainly isn't the only thing we need to check to make sure the * inodes are consistent. However, this seems to be the only thing that * the MS implementation checks when working around its own bug. * * (Tested: If two dentries share the same hard link group ID, Windows * 8.1 DISM will link them if they have the same unnamed stream hash, * even if the dentries provide different timestamps, attributes, * alternate data streams, and security IDs! And the one that gets used * will change if you merely swap the filenames. But if you use * different unnamed stream hashes with everything else the same, it * doesn't link the dentries.) * * For non-buggy WIMs this function will always return true. */ return hashes_equal(inode_get_hash_of_unnamed_data_stream(inode_1), inode_get_hash_of_unnamed_data_stream(inode_2)); } static int inode_table_insert(struct wim_dentry *dentry, void *_params) { struct inode_fixup_params *params = _params; struct wim_inode_table *table = ¶ms->inode_table; struct wim_inode *d_inode = dentry->d_inode; size_t pos; struct wim_inode *inode; if (d_inode->i_ino == 0) { hlist_add_head(&d_inode->i_hlist_node, &table->extra_inodes); return 0; } /* Try adding this dentry to an existing inode. */ pos = hash_inode(table, d_inode->i_ino, 0); hlist_for_each_entry(inode, &table->array[pos], i_hlist_node) { if (inode->i_ino != d_inode->i_ino) { continue; } if (unlikely(!inodes_consistent(inode, d_inode))) { params->num_inconsistent_inodes++; continue; } if (unlikely((d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) || (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY))) { params->num_dir_hard_links++; if (params->num_dir_hard_links <= MAX_DIR_HARD_LINK_WARNINGS) { WARNING("Unsupported directory hard link " "\"%"TS"\" <=> \"%"TS"\"", dentry_full_path(dentry), inode_any_full_path(inode)); } else if (params->num_dir_hard_links == MAX_DIR_HARD_LINK_WARNINGS + 1) { WARNING("Suppressing additional warnings about " "directory hard links..."); } continue; } /* Transfer this dentry to the existing inode. */ d_disassociate(dentry); d_associate(dentry, inode); return 0; } /* Keep this dentry's inode. */ hlist_add_head(&d_inode->i_hlist_node, &table->array[pos]); if (++table->filled > table->capacity) enlarge_inode_table(table); return 0; } static void hlist_move_all(struct hlist_head *src, struct hlist_head *dest) { struct hlist_node *node; while ((node = src->first) != NULL) { hlist_del(node); hlist_add_head(node, dest); } } /* Move the inodes from the 'struct wim_inode_table' to the 'inode_list'. */ static void build_inode_list(struct wim_inode_table *inode_table, struct hlist_head *inode_list) { hlist_move_all(&inode_table->extra_inodes, inode_list); for (size_t i = 0; i < inode_table->capacity; i++) hlist_move_all(&inode_table->array[i], inode_list); } /* Re-assign inode numbers to the inodes in the list. */ static void reassign_inode_numbers(struct hlist_head *inode_list) { struct wim_inode *inode; u64 cur_ino = 1; hlist_for_each_entry(inode, inode_list, i_hlist_node) inode->i_ino = cur_ino++; } /* * Given a WIM image's tree of dentries such that each dentry initially * has a unique inode associated with it, determine the actual * dentry/inode information. Following this, a single inode may be named * by more than one dentry (usually called a hard link). * * The 'hard_link_group_id' field of the on-disk WIM dentry, which we * have read into 'i_ino' of each dentry's initial inode, determines * which dentries share the same inode. Ideally, dentries share the same * inode if and only if they have the same value in this field. However, * exceptions apply: * * - If 'hard_link_group_id' is 0, the corresponding dentry is the sole * name for its inode. * - Due to bugs in the Microsoft implementation, dentries with different * 'hard_link_group_id' fields may, in fact, need to be interpreted as * naming different inodes. This seems to mostly affect images in * install.wim for Windows 7. I try to work around this in the same way * the Microsoft implementation works around this. * * Returns 0 or WIMLIB_ERR_NOMEM. On success, the resulting inodes will be * appended to the @inode_list, and they will have consistent numbers in their * i_ino fields. */ int dentry_tree_fix_inodes(struct wim_dentry *root, struct hlist_head *inode_list) { struct inode_fixup_params params; int ret; /* We use a hash table to map inode numbers to inodes. */ ret = init_inode_table(¶ms.inode_table, 64); if (ret) return ret; params.num_dir_hard_links = 0; params.num_inconsistent_inodes = 0; for_dentry_in_tree(root, inode_table_insert, ¶ms); /* Generate the resulting list of inodes, and if needed reassign * the inode numbers. */ build_inode_list(¶ms.inode_table, inode_list); destroy_inode_table(¶ms.inode_table); if (unlikely(params.num_dir_hard_links)) WARNING("Ignoring %lu directory hard links", params.num_dir_hard_links); if (unlikely(params.num_inconsistent_inodes || params.num_dir_hard_links)) reassign_inode_numbers(inode_list); return 0; } wimlib-1.13.1/src/win32_apply.c0000644000175000017500000030461613401642226013115 00000000000000/* * win32_apply.c - Windows-specific code for applying files from a WIM image. */ /* * Copyright (C) 2013-2018 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef __WIN32__ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib/win32_common.h" #include "wimlib/apply.h" #include "wimlib/assert.h" #include "wimlib/blob_table.h" #include "wimlib/dentry.h" #include "wimlib/encoding.h" #include "wimlib/error.h" #include "wimlib/metadata.h" #include "wimlib/object_id.h" #include "wimlib/paths.h" #include "wimlib/pattern.h" #include "wimlib/reparse.h" #include "wimlib/scan.h" /* for mangle_pat() and match_pattern_list() */ #include "wimlib/textfile.h" #include "wimlib/wimboot.h" #include "wimlib/wof.h" #include "wimlib/xattr.h" #include "wimlib/xml.h" struct win32_apply_ctx { /* Extract flags, the pointer to the WIMStruct, etc. */ struct apply_ctx common; /* WIMBoot information, only filled in if WIMLIB_EXTRACT_FLAG_WIMBOOT * was provided */ struct { /* This array contains the WIM files registered with WOF on the * target volume for this extraction operation. All WIMStructs * in this array are distinct and have ->filename != NULL. */ struct wimboot_wim { WIMStruct *wim; u64 data_source_id; u8 blob_table_hash[SHA1_HASH_SIZE]; } *wims; size_t num_wims; bool wof_running; bool have_wrong_version_wims; bool have_uncompressed_wims; bool have_unsupported_compressed_resources; bool have_huge_resources; } wimboot; /* External backing information */ struct string_list *prepopulate_pats; void *mem_prepopulate_pats; bool tried_to_load_prepopulate_list; /* Open handle to the target directory */ HANDLE h_target; /* NT namespace path to the target directory (buffer allocated) */ UNICODE_STRING target_ntpath; /* Temporary buffer for building paths (buffer allocated) */ UNICODE_STRING pathbuf; /* Object attributes to reuse for opening files in the target directory. * (attr.ObjectName == &pathbuf) and (attr.RootDirectory == h_target). */ OBJECT_ATTRIBUTES attr; /* Temporary I/O status block for system calls */ IO_STATUS_BLOCK iosb; /* Allocated buffer for creating "printable" paths from our * target-relative NT paths */ wchar_t *print_buffer; /* Allocated buffer for reading blob data when it cannot be extracted * directly */ u8 *data_buffer; /* Pointer to the next byte in @data_buffer to fill */ u8 *data_buffer_ptr; /* Size allocated in @data_buffer */ size_t data_buffer_size; /* Current offset in the raw encrypted file being written */ size_t encrypted_offset; /* Current size of the raw encrypted file being written */ size_t encrypted_size; /* Temporary buffer for reparse data */ struct reparse_buffer_disk rpbuf; /* Temporary buffer for reparse data of "fixed" absolute symbolic links * and junctions */ struct reparse_buffer_disk rpfixbuf; /* Array of open handles to filesystem streams currently being written */ HANDLE open_handles[MAX_OPEN_FILES]; /* Number of handles in @open_handles currently open (filled in from the * beginning of the array) */ unsigned num_open_handles; /* For each currently open stream, whether we're writing to it in * "sparse" mode or not. */ bool is_sparse_stream[MAX_OPEN_FILES]; /* Whether is_sparse_stream[] is true for any currently open stream */ bool any_sparse_streams; /* List of dentries, joined by @d_tmp_list, that need to have reparse * data extracted as soon as the whole blob has been read into * @data_buffer. */ struct list_head reparse_dentries; /* List of dentries, joined by @d_tmp_list, that need to have raw * encrypted data extracted as soon as the whole blob has been read into * @data_buffer. */ struct list_head encrypted_dentries; /* Number of files for which we didn't have permission to set the full * security descriptor. */ unsigned long partial_security_descriptors; /* Number of files for which we didn't have permission to set any part * of the security descriptor. */ unsigned long no_security_descriptors; /* Number of files for which we couldn't set the short name. */ unsigned long num_set_short_name_failures; /* Number of files for which we couldn't remove the short name. */ unsigned long num_remove_short_name_failures; /* Number of files on which we couldn't set System Compression. */ unsigned long num_system_compression_failures; /* The number of files which, for compatibility with the Windows * bootloader, were not compressed using the requested system * compression format. This includes matches with the hardcoded pattern * list only; it does not include matches with patterns in * [PrepopulateList]. */ unsigned long num_system_compression_exclusions; /* Number of files for which we couldn't set the object ID. */ unsigned long num_object_id_failures; /* Number of files for which we couldn't set extended attributes. */ unsigned long num_xattr_failures; /* The Windows build number of the image being applied, or 0 if unknown. */ u64 windows_build_number; /* Have we tried to enable short name support on the target volume yet? */ bool tried_to_enable_short_names; }; /* Get the drive letter from a Windows path, or return the null character if the * path is relative. */ static wchar_t get_drive_letter(const wchar_t *path) { /* Skip \\?\ prefix */ if (!wcsncmp(path, L"\\\\?\\", 4)) path += 4; /* Return drive letter if valid */ if (((path[0] >= L'a' && path[0] <= L'z') || (path[0] >= L'A' && path[0] <= L'Z')) && path[1] == L':') return path[0]; return L'\0'; } static void get_vol_flags(const wchar_t *target, DWORD *vol_flags_ret, bool *short_names_supported_ret) { wchar_t filesystem_name[MAX_PATH + 1]; wchar_t drive[4]; wchar_t *volume = NULL; *vol_flags_ret = 0; *short_names_supported_ret = false; drive[0] = get_drive_letter(target); if (drive[0]) { drive[1] = L':'; drive[2] = L'\\'; drive[3] = L'\0'; volume = drive; } if (!GetVolumeInformation(volume, NULL, 0, NULL, NULL, vol_flags_ret, filesystem_name, ARRAY_LEN(filesystem_name))) { win32_warning(GetLastError(), L"Failed to get volume information for \"%ls\"", target); return; } if (wcsstr(filesystem_name, L"NTFS")) { /* * FILE_SUPPORTS_HARD_LINKS and * FILE_SUPPORTS_EXTENDED_ATTRIBUTES are only supported on * Windows 7 and later. Force them on anyway if the filesystem * is NTFS. */ *vol_flags_ret |= FILE_SUPPORTS_HARD_LINKS; *vol_flags_ret |= FILE_SUPPORTS_EXTENDED_ATTRIBUTES; /* There's no volume flag for short names, but according to the * MS documentation they are only user-settable on NTFS. */ *short_names_supported_ret = true; } } /* Is the image being extracted an OS image for Windows 10 or later? */ static bool is_image_windows_10_or_later(struct win32_apply_ctx *ctx) { /* Note: if no build number is available, this returns false. */ return ctx->windows_build_number >= 10240; } static const wchar_t * current_path(struct win32_apply_ctx *ctx); static void build_extraction_path(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx); static int report_dentry_apply_error(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx, int ret) { build_extraction_path(dentry, ctx); return report_apply_error(&ctx->common, ret, current_path(ctx)); } static inline int check_apply_error(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx, int ret) { if (unlikely(ret)) ret = report_dentry_apply_error(dentry, ctx, ret); return ret; } static int win32_get_supported_features(const wchar_t *target, struct wim_features *supported_features) { DWORD vol_flags; bool short_names_supported; /* Query the features of the target volume. */ get_vol_flags(target, &vol_flags, &short_names_supported); supported_features->readonly_files = 1; supported_features->hidden_files = 1; supported_features->system_files = 1; supported_features->archive_files = 1; if (vol_flags & FILE_FILE_COMPRESSION) supported_features->compressed_files = 1; if (vol_flags & FILE_SUPPORTS_ENCRYPTION) { supported_features->encrypted_files = 1; supported_features->encrypted_directories = 1; } supported_features->not_context_indexed_files = 1; if (vol_flags & FILE_SUPPORTS_SPARSE_FILES) supported_features->sparse_files = 1; if (vol_flags & FILE_NAMED_STREAMS) supported_features->named_data_streams = 1; if (vol_flags & FILE_SUPPORTS_HARD_LINKS) supported_features->hard_links = 1; if (vol_flags & FILE_SUPPORTS_REPARSE_POINTS) supported_features->reparse_points = 1; if (vol_flags & FILE_PERSISTENT_ACLS) supported_features->security_descriptors = 1; if (short_names_supported) supported_features->short_names = 1; if (vol_flags & FILE_SUPPORTS_OBJECT_IDS) supported_features->object_ids = 1; supported_features->timestamps = 1; if (vol_flags & FILE_CASE_SENSITIVE_SEARCH) { /* * The filesystem supports case-sensitive filenames. But does * the operating system as well? This normally requires the * registry setting ObCaseInsensitive=0. We can test it * indirectly by attempting to open the "\SystemRoot" symbolic * link using a name with the wrong case. If we get * STATUS_OBJECT_NAME_NOT_FOUND instead of STATUS_ACCESS_DENIED, * then case-sensitive names must be enabled. */ UNICODE_STRING path; OBJECT_ATTRIBUTES attr; HANDLE h; NTSTATUS status; RtlInitUnicodeString(&path, L"\\systemroot"); InitializeObjectAttributes(&attr, &path, 0, NULL, NULL); status = NtOpenSymbolicLinkObject(&h, 0, &attr); if (status == STATUS_OBJECT_NAME_NOT_FOUND) supported_features->case_sensitive_filenames = 1; } if (vol_flags & FILE_SUPPORTS_EXTENDED_ATTRIBUTES) supported_features->xattrs = 1; return 0; } #define COMPACT_FLAGS (WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K | \ WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K | \ WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K | \ WIMLIB_EXTRACT_FLAG_COMPACT_LZX) /* * If not done already, load the patterns from the [PrepopulateList] section of * WimBootCompress.ini in the WIM image being extracted. * * Note: WimBootCompress.ini applies to both types of "external backing": * * - WIM backing ("WIMBoot" - Windows 8.1 and later) * - File backing ("System Compression" - Windows 10 and later) */ static int load_prepopulate_pats(struct win32_apply_ctx *ctx) { const wchar_t *path = L"\\Windows\\System32\\WimBootCompress.ini"; struct wim_dentry *dentry; const struct blob_descriptor *blob; int ret; void *buf; struct string_list *strings; void *mem; struct text_file_section sec; if (ctx->tried_to_load_prepopulate_list) return 0; ctx->tried_to_load_prepopulate_list = true; dentry = get_dentry(ctx->common.wim, path, WIMLIB_CASE_INSENSITIVE); if (!dentry || (dentry->d_inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_ENCRYPTED)) || !(blob = inode_get_blob_for_unnamed_data_stream(dentry->d_inode, ctx->common.wim->blob_table))) { WARNING("%ls does not exist in the WIM image.\n" " The default configuration will be used instead; it assumes that all\n" " files are valid for external backing regardless of path, equivalent\n" " to an empty [PrepopulateList] section.", path); return WIMLIB_ERR_PATH_DOES_NOT_EXIST; } ret = read_blob_into_alloc_buf(blob, &buf); if (ret) return ret; strings = CALLOC(1, sizeof(struct string_list)); if (!strings) { FREE(buf); return WIMLIB_ERR_NOMEM; } sec.name = T("PrepopulateList"); sec.strings = strings; ret = load_text_file(path, buf, blob->size, &mem, &sec, 1, LOAD_TEXT_FILE_REMOVE_QUOTES | LOAD_TEXT_FILE_NO_WARNINGS, mangle_pat); STATIC_ASSERT(OS_PREFERRED_PATH_SEPARATOR == WIM_PATH_SEPARATOR); FREE(buf); if (ret) { FREE(strings); return ret; } ctx->prepopulate_pats = strings; ctx->mem_prepopulate_pats = mem; return 0; } /* Returns %true if the specified absolute path to a file in the WIM image can * be subject to external backing when extracted. Otherwise returns %false. */ static bool can_externally_back_path(const wchar_t *path, const struct win32_apply_ctx *ctx) { /* Does the path match a pattern given in the [PrepopulateList] section * of WimBootCompress.ini? */ if (ctx->prepopulate_pats && match_pattern_list(path, ctx->prepopulate_pats, MATCH_RECURSIVELY)) return false; /* Since we attempt to modify the SYSTEM registry after it's extracted * (see end_wimboot_extraction()), it can't be extracted as externally * backed. This extends to associated files such as SYSTEM.LOG that * also must be writable in order to write to the registry. Normally, * SYSTEM is in [PrepopulateList], and the SYSTEM.* files match patterns * in [ExclusionList] and therefore are not captured in the WIM at all. * However, a WIM that wasn't specifically captured in "WIMBoot mode" * may contain SYSTEM.* files. So to make things "just work", hard-code * the pattern. */ if (match_path(path, L"\\Windows\\System32\\config\\SYSTEM*", 0)) return false; return true; } /* Can the specified WIM resource be used as the source of an external backing * for the wof.sys WIM provider? */ static bool is_resource_valid_for_external_backing(const struct wim_resource_descriptor *rdesc, struct win32_apply_ctx *ctx) { /* Must be the original WIM file format. This check excludes pipable * resources and solid resources. It also excludes other resources * contained in such files even if they would be otherwise compatible. */ if (rdesc->wim->hdr.magic != WIM_MAGIC || rdesc->wim->hdr.wim_version != WIM_VERSION_DEFAULT) { ctx->wimboot.have_wrong_version_wims = true; return false; } /* * Whitelist of compression types and chunk sizes supported by * Microsoft's WOF driver. * * Notes: * - Uncompressed WIMs result in BSOD. However, this only applies to * the WIM file itself, not to uncompressed resources in a WIM file * that is otherwise compressed. * - XPRESS 64K sometimes appears to work, but sometimes it causes * reads to fail with STATUS_UNSUCCESSFUL. */ switch (rdesc->compression_type) { case WIMLIB_COMPRESSION_TYPE_NONE: if (rdesc->wim->compression_type == WIMLIB_COMPRESSION_TYPE_NONE) { ctx->wimboot.have_uncompressed_wims = true; return false; } break; case WIMLIB_COMPRESSION_TYPE_XPRESS: switch (rdesc->chunk_size) { case 4096: case 8192: case 16384: case 32768: break; default: ctx->wimboot.have_unsupported_compressed_resources = true; return false; } break; case WIMLIB_COMPRESSION_TYPE_LZX: switch (rdesc->chunk_size) { case 32768: break; default: ctx->wimboot.have_unsupported_compressed_resources = true; return false; } break; default: ctx->wimboot.have_unsupported_compressed_resources = true; return false; } /* Microsoft's WoF driver errors out if it tries to satisfy a read with * ending offset >= 4 GiB from an externally backed file. */ if (rdesc->uncompressed_size > 4200000000) { ctx->wimboot.have_huge_resources = true; return false; } return true; } #define EXTERNAL_BACKING_NOT_ENABLED -1 #define EXTERNAL_BACKING_NOT_POSSIBLE -2 #define EXTERNAL_BACKING_EXCLUDED -3 /* * Determines whether the specified file will be externally backed. Returns a * negative status code if no, 0 if yes, or a positive wimlib error code on * error. If the file is excluded from external backing based on its path, then * *excluded_dentry_ret is set to the dentry for the path that matched the * exclusion rule. * * Note that this logic applies to both types of "external backing": * * - WIM backing ("WIMBoot" - Windows 8.1 and later) * - File backing ("System Compression" - Windows 10 and later) * * However, in the case of WIM backing we also need to validate that the WIM * resource that would be the source of the backing is supported by the wof.sys * WIM provider. */ static int will_externally_back_inode(struct wim_inode *inode, struct win32_apply_ctx *ctx, const struct wim_dentry **excluded_dentry_ret, bool wimboot_mode) { struct wim_dentry *dentry; struct blob_descriptor *blob; int ret; if (load_prepopulate_pats(ctx) == WIMLIB_ERR_NOMEM) return WIMLIB_ERR_NOMEM; if (inode->i_can_externally_back) return 0; /* This may do redundant checks because the cached value * i_can_externally_back is 2-state (as opposed to 3-state: * unknown/no/yes). But most files can be externally backed, so this * way is fine. */ if (inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_ENCRYPTED)) return EXTERNAL_BACKING_NOT_POSSIBLE; blob = inode_get_blob_for_unnamed_data_stream_resolved(inode); if (!blob) return EXTERNAL_BACKING_NOT_POSSIBLE; if (wimboot_mode && (blob->blob_location != BLOB_IN_WIM || !is_resource_valid_for_external_backing(blob->rdesc, ctx))) return EXTERNAL_BACKING_NOT_POSSIBLE; /* * We need to check the patterns in [PrepopulateList] against every name * of the inode, in case any of them match. */ inode_for_each_extraction_alias(dentry, inode) { ret = calculate_dentry_full_path(dentry); if (ret) return ret; if (!can_externally_back_path(dentry->d_full_path, ctx)) { if (excluded_dentry_ret) *excluded_dentry_ret = dentry; return EXTERNAL_BACKING_EXCLUDED; } } inode->i_can_externally_back = 1; return 0; } /* * Determines if the unnamed data stream of a file will be created as a WIM * external backing (a "WIMBoot pointer file"), as opposed to a standard * extraction. */ static int win32_will_back_from_wim(struct wim_dentry *dentry, struct apply_ctx *_ctx) { struct win32_apply_ctx *ctx = (struct win32_apply_ctx *)_ctx; if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT)) return EXTERNAL_BACKING_NOT_ENABLED; return will_externally_back_inode(dentry->d_inode, ctx, NULL, true); } /* Find the WOF registration information for the specified WIM file. */ static struct wimboot_wim * find_wimboot_wim(WIMStruct *wim_to_find, struct win32_apply_ctx *ctx) { for (size_t i = 0; i < ctx->wimboot.num_wims; i++) if (wim_to_find == ctx->wimboot.wims[i].wim) return &ctx->wimboot.wims[i]; wimlib_assert(0); return NULL; } static int set_backed_from_wim(HANDLE h, struct wim_inode *inode, struct win32_apply_ctx *ctx) { int ret; const struct wim_dentry *excluded_dentry; const struct blob_descriptor *blob; const struct wimboot_wim *wimboot_wim; ret = will_externally_back_inode(inode, ctx, &excluded_dentry, true); if (ret > 0) /* Error. */ return ret; if (ret < 0 && ret != EXTERNAL_BACKING_EXCLUDED) return 0; /* Not externally backing, other than due to exclusion. */ if (unlikely(ret == EXTERNAL_BACKING_EXCLUDED)) { /* Not externally backing due to exclusion. */ union wimlib_progress_info info; build_extraction_path(excluded_dentry, ctx); info.wimboot_exclude.path_in_wim = excluded_dentry->d_full_path; info.wimboot_exclude.extraction_path = current_path(ctx); return call_progress(ctx->common.progfunc, WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE, &info, ctx->common.progctx); } /* Externally backing. */ blob = inode_get_blob_for_unnamed_data_stream_resolved(inode); wimboot_wim = find_wimboot_wim(blob->rdesc->wim, ctx); if (unlikely(!wimboot_set_pointer(h, blob, wimboot_wim->data_source_id, wimboot_wim->blob_table_hash, ctx->wimboot.wof_running))) { const DWORD err = GetLastError(); build_extraction_path(inode_first_extraction_dentry(inode), ctx); win32_error(err, L"\"%ls\": Couldn't set WIMBoot pointer data", current_path(ctx)); return WIMLIB_ERR_WIMBOOT; } return 0; } /* Calculates the SHA-1 message digest of the WIM's blob table. */ static int hash_blob_table(WIMStruct *wim, u8 hash[SHA1_HASH_SIZE]) { return wim_reshdr_to_hash(&wim->hdr.blob_table_reshdr, wim, hash); } static int register_wim_with_wof(WIMStruct *wim, struct win32_apply_ctx *ctx) { struct wimboot_wim *p; int ret; /* Check if already registered */ for (size_t i = 0; i < ctx->wimboot.num_wims; i++) if (wim == ctx->wimboot.wims[i].wim) return 0; /* Not yet registered */ p = REALLOC(ctx->wimboot.wims, (ctx->wimboot.num_wims + 1) * sizeof(ctx->wimboot.wims[0])); if (!p) return WIMLIB_ERR_NOMEM; ctx->wimboot.wims = p; ctx->wimboot.wims[ctx->wimboot.num_wims].wim = wim; ret = hash_blob_table(wim, ctx->wimboot.wims[ctx->wimboot.num_wims].blob_table_hash); if (ret) return ret; ret = wimboot_alloc_data_source_id(wim->filename, wim->hdr.guid, ctx->common.wim->current_image, ctx->common.target, &ctx->wimboot.wims[ctx->wimboot.num_wims].data_source_id, &ctx->wimboot.wof_running); if (ret) return ret; ctx->wimboot.num_wims++; return 0; } /* Prepare for doing a "WIMBoot" extraction by registering each source WIM file * with WOF on the target volume. */ static int start_wimboot_extraction(struct list_head *dentry_list, struct win32_apply_ctx *ctx) { int ret; struct wim_dentry *dentry; if (!xml_get_wimboot(ctx->common.wim->xml_info, ctx->common.wim->current_image)) WARNING("The WIM image is not marked as WIMBoot compatible. This usually\n" " means it is not intended to be used to back a Windows operating\n" " system. Proceeding anyway."); list_for_each_entry(dentry, dentry_list, d_extraction_list_node) { struct blob_descriptor *blob; ret = win32_will_back_from_wim(dentry, &ctx->common); if (ret > 0) /* Error */ return ret; if (ret < 0) /* Won't externally back */ continue; blob = inode_get_blob_for_unnamed_data_stream_resolved(dentry->d_inode); ret = register_wim_with_wof(blob->rdesc->wim, ctx); if (ret) return ret; } if (ctx->wimboot.have_wrong_version_wims) { WARNING("At least one of the source WIM files uses a version of the WIM\n" " file format that not supported by Microsoft's wof.sys driver.\n" " Files whose data is contained in one of these WIM files will be\n" " extracted as full files rather than externally backed."); } if (ctx->wimboot.have_uncompressed_wims) { WARNING("At least one of the source WIM files is uncompressed. Files whose\n" " data is contained in an uncompressed WIM file will be extracted as\n" " full files rather than externally backed, since uncompressed WIM\n" " files are not supported by Microsoft's wof.sys driver."); } if (ctx->wimboot.have_unsupported_compressed_resources) { WARNING("At least one of the source WIM files uses a compression format that\n" " is not supported by Microsoft's wof.sys driver. Files whose data is\n" " contained in a compressed resource in one of these WIM files will be\n" " extracted as full files rather than externally backed. (The\n" " compression formats supported by wof.sys are: XPRESS 4K, XPRESS 8K,\n" " XPRESS 16K, XPRESS 32K, and LZX 32K.)"); } if (ctx->wimboot.have_huge_resources) { WARNING("Some files exceeded 4.2 GB in size. Such files will be extracted\n" " as full files rather than externally backed, since very large files\n" " are not supported by Microsoft's wof.sys driver."); } return 0; } static void build_win32_extraction_path(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx); /* Sets WimBoot=1 in the extracted SYSTEM registry hive. * * WIMGAPI does this, and it's possible that it's important. * But I don't know exactly what this value means to Windows. */ static int end_wimboot_extraction(struct win32_apply_ctx *ctx) { struct wim_dentry *dentry; wchar_t subkeyname[32]; LONG res; LONG res2; HKEY key; DWORD value; dentry = get_dentry(ctx->common.wim, L"\\Windows\\System32\\config\\SYSTEM", WIMLIB_CASE_INSENSITIVE); if (!dentry || !will_extract_dentry(dentry)) goto out; if (!will_extract_dentry(wim_get_current_root_dentry(ctx->common.wim))) goto out; /* Not bothering to use the native routines (e.g. NtLoadKey()) for this. * If this doesn't work, you probably also have many other problems. */ build_win32_extraction_path(dentry, ctx); get_random_alnum_chars(subkeyname, 20); subkeyname[20] = L'\0'; res = RegLoadKey(HKEY_LOCAL_MACHINE, subkeyname, ctx->pathbuf.Buffer); if (res) goto out_check_res; wcscpy(&subkeyname[20], L"\\Setup"); res = RegCreateKeyEx(HKEY_LOCAL_MACHINE, subkeyname, 0, NULL, REG_OPTION_BACKUP_RESTORE, 0, NULL, &key, NULL); if (res) goto out_unload_key; value = 1; res = RegSetValueEx(key, L"WimBoot", 0, REG_DWORD, (const BYTE *)&value, sizeof(DWORD)); if (res) goto out_close_key; res = RegFlushKey(key); out_close_key: res2 = RegCloseKey(key); if (!res) res = res2; out_unload_key: subkeyname[20] = L'\0'; RegUnLoadKey(HKEY_LOCAL_MACHINE, subkeyname); out_check_res: if (res) { /* Warning only. */ win32_warning(res, L"Failed to set \\Setup: dword \"WimBoot\"=1 " "value in registry hive \"%ls\"", ctx->pathbuf.Buffer); } out: return 0; } /* Returns the number of wide characters needed to represent the path to the * specified @dentry, relative to the target directory, when extracted. * * Does not include null terminator (not needed for NtCreateFile). */ static size_t dentry_extraction_path_length(const struct wim_dentry *dentry) { size_t len = 0; const struct wim_dentry *d; d = dentry; do { len += d->d_extraction_name_nchars + 1; d = d->d_parent; } while (!dentry_is_root(d) && will_extract_dentry(d)); return --len; /* No leading slash */ } /* Returns the length of the longest string that might need to be appended to * the path to an alias of an inode to open or create a named data stream. * * If the inode has no named data streams, this will be 0. Otherwise, this will * be 1 plus the length of the longest-named data stream, since the data stream * name must be separated from the path by the ':' character. */ static size_t inode_longest_named_data_stream_spec(const struct wim_inode *inode) { size_t max = 0; for (unsigned i = 0; i < inode->i_num_streams; i++) { const struct wim_inode_stream *strm = &inode->i_streams[i]; if (!stream_is_named_data_stream(strm)) continue; size_t len = utf16le_len_chars(strm->stream_name); if (len > max) max = len; } if (max) max += 1; return max; } /* Find the length, in wide characters, of the longest path needed for * extraction of any file in @dentry_list relative to the target directory. * * Accounts for named data streams, but does not include null terminator (not * needed for NtCreateFile). */ static size_t compute_path_max(struct list_head *dentry_list) { size_t max = 0; const struct wim_dentry *dentry; list_for_each_entry(dentry, dentry_list, d_extraction_list_node) { size_t len; len = dentry_extraction_path_length(dentry); /* Account for named data streams */ len += inode_longest_named_data_stream_spec(dentry->d_inode); if (len > max) max = len; } return max; } /* Build the path at which to extract the @dentry, relative to the target * directory. * * The path is saved in ctx->pathbuf. */ static void build_extraction_path(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx) { size_t len; wchar_t *p; const struct wim_dentry *d; len = dentry_extraction_path_length(dentry); ctx->pathbuf.Length = len * sizeof(wchar_t); p = ctx->pathbuf.Buffer + len; for (d = dentry; !dentry_is_root(d->d_parent) && will_extract_dentry(d->d_parent); d = d->d_parent) { p -= d->d_extraction_name_nchars; if (d->d_extraction_name_nchars) wmemcpy(p, d->d_extraction_name, d->d_extraction_name_nchars); *--p = '\\'; } /* No leading slash */ p -= d->d_extraction_name_nchars; wmemcpy(p, d->d_extraction_name, d->d_extraction_name_nchars); } /* Build the path at which to extract the @dentry, relative to the target * directory, adding the suffix for a named data stream. * * The path is saved in ctx->pathbuf. */ static void build_extraction_path_with_ads(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx, const wchar_t *stream_name, size_t stream_name_nchars) { wchar_t *p; build_extraction_path(dentry, ctx); /* Add :NAME for named data stream */ p = ctx->pathbuf.Buffer + (ctx->pathbuf.Length / sizeof(wchar_t)); *p++ = L':'; wmemcpy(p, stream_name, stream_name_nchars); ctx->pathbuf.Length += (1 + stream_name_nchars) * sizeof(wchar_t); } /* Build the Win32 namespace path to the specified @dentry when extracted. * * The path is saved in ctx->pathbuf and will be null terminated. * * XXX: We could get rid of this if it wasn't needed for the file encryption * APIs, and the registry manipulation in WIMBoot mode. */ static void build_win32_extraction_path(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx) { build_extraction_path(dentry, ctx); /* Prepend target_ntpath to our relative path, then change \??\ into \\?\ */ memmove(ctx->pathbuf.Buffer + (ctx->target_ntpath.Length / sizeof(wchar_t)) + 1, ctx->pathbuf.Buffer, ctx->pathbuf.Length); memcpy(ctx->pathbuf.Buffer, ctx->target_ntpath.Buffer, ctx->target_ntpath.Length); ctx->pathbuf.Buffer[ctx->target_ntpath.Length / sizeof(wchar_t)] = L'\\'; ctx->pathbuf.Length += ctx->target_ntpath.Length + sizeof(wchar_t); ctx->pathbuf.Buffer[ctx->pathbuf.Length / sizeof(wchar_t)] = L'\0'; wimlib_assert(ctx->pathbuf.Length >= 4 * sizeof(wchar_t) && !wmemcmp(ctx->pathbuf.Buffer, L"\\??\\", 4)); ctx->pathbuf.Buffer[1] = L'\\'; } /* Returns a "printable" representation of the last relative NT path that was * constructed with build_extraction_path() or build_extraction_path_with_ads(). * * This will be overwritten by the next call to this function. */ static const wchar_t * current_path(struct win32_apply_ctx *ctx) { wchar_t *p = ctx->print_buffer; p = wmempcpy(p, ctx->common.target, ctx->common.target_nchars); *p++ = L'\\'; p = wmempcpy(p, ctx->pathbuf.Buffer, ctx->pathbuf.Length / sizeof(wchar_t)); *p = L'\0'; return ctx->print_buffer; } /* Open handle to the target directory if it is not already open. If the target * directory does not exist, this creates it. */ static int open_target_directory(struct win32_apply_ctx *ctx) { NTSTATUS status; if (ctx->h_target) return 0; ctx->attr.Length = sizeof(ctx->attr); ctx->attr.RootDirectory = NULL; ctx->attr.ObjectName = &ctx->target_ntpath; /* Don't use FILE_OPEN_REPARSE_POINT here; we want the extraction to * happen at the directory "pointed to" by the reparse point. */ status = NtCreateFile(&ctx->h_target, FILE_TRAVERSE, &ctx->attr, &ctx->iosb, NULL, 0, FILE_SHARE_VALID_FLAGS, FILE_OPEN_IF, FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT, NULL, 0); if (!NT_SUCCESS(status)) { winnt_error(status, L"Can't open or create directory \"%ls\"", ctx->common.target); return WIMLIB_ERR_OPENDIR; } ctx->attr.RootDirectory = ctx->h_target; ctx->attr.ObjectName = &ctx->pathbuf; return 0; } static void close_target_directory(struct win32_apply_ctx *ctx) { if (ctx->h_target) { NtClose(ctx->h_target); ctx->h_target = NULL; ctx->attr.RootDirectory = NULL; } } /* * Ensures the target directory exists and opens a handle to it, in preparation * of using paths relative to it. */ static int prepare_target(struct list_head *dentry_list, struct win32_apply_ctx *ctx) { int ret; size_t path_max; ret = win32_path_to_nt_path(ctx->common.target, &ctx->target_ntpath); if (ret) return ret; ret = open_target_directory(ctx); if (ret) return ret; path_max = compute_path_max(dentry_list); /* Add some extra for building Win32 paths for the file encryption APIs, * and ensure we have at least enough to potentially use a 8.3 name for * the last component. */ path_max += max(2 + (ctx->target_ntpath.Length / sizeof(wchar_t)), 8 + 1 + 3); ctx->pathbuf.MaximumLength = path_max * sizeof(wchar_t); if (ctx->pathbuf.MaximumLength != path_max * sizeof(wchar_t)) { /* Paths are too long for a UNICODE_STRING! */ ERROR("Some paths are too long to extract (> 32768 characters)!"); return WIMLIB_ERR_UNSUPPORTED; } ctx->pathbuf.Buffer = MALLOC(ctx->pathbuf.MaximumLength); if (!ctx->pathbuf.Buffer) return WIMLIB_ERR_NOMEM; ctx->print_buffer = MALLOC((ctx->common.target_nchars + 1 + path_max + 1) * sizeof(wchar_t)); if (!ctx->print_buffer) return WIMLIB_ERR_NOMEM; return 0; } /* When creating an inode that will have a short (DOS) name, we create it using * the long name associated with the short name. This ensures that the short * name gets associated with the correct long name. */ static struct wim_dentry * first_extraction_alias(const struct wim_inode *inode) { struct wim_dentry *dentry; inode_for_each_extraction_alias(dentry, inode) if (dentry_has_short_name(dentry)) return dentry; return inode_first_extraction_dentry(inode); } /* * Set or clear FILE_ATTRIBUTE_COMPRESSED if the inherited value is different * from the desired value. * * Note that you can NOT override the inherited value of * FILE_ATTRIBUTE_COMPRESSED directly with NtCreateFile(). */ static int adjust_compression_attribute(HANDLE h, const struct wim_dentry *dentry, struct win32_apply_ctx *ctx) { const bool compressed = (dentry->d_inode->i_attributes & FILE_ATTRIBUTE_COMPRESSED); FILE_BASIC_INFORMATION info; USHORT compression_state; NTSTATUS status; if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES) return 0; if (!ctx->common.supported_features.compressed_files) return 0; /* Get current attributes */ status = NtQueryInformationFile(h, &ctx->iosb, &info, sizeof(info), FileBasicInformation); if (NT_SUCCESS(status) && compressed == !!(info.FileAttributes & FILE_ATTRIBUTE_COMPRESSED)) { /* Nothing needs to be done. */ return 0; } /* Set the new compression state */ if (compressed) compression_state = COMPRESSION_FORMAT_DEFAULT; else compression_state = COMPRESSION_FORMAT_NONE; status = winnt_fsctl(h, FSCTL_SET_COMPRESSION, &compression_state, sizeof(USHORT), NULL, 0, NULL); if (NT_SUCCESS(status)) return 0; winnt_error(status, L"Can't %s compression attribute on \"%ls\"", (compressed ? "set" : "clear"), current_path(ctx)); return WIMLIB_ERR_SET_ATTRIBUTES; } static bool need_sparse_flag(const struct wim_inode *inode, const struct win32_apply_ctx *ctx) { return (inode->i_attributes & FILE_ATTRIBUTE_SPARSE_FILE) && ctx->common.supported_features.sparse_files; } static int set_sparse_flag(HANDLE h, struct win32_apply_ctx *ctx) { NTSTATUS status; status = winnt_fsctl(h, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, NULL); if (NT_SUCCESS(status)) return 0; winnt_error(status, L"Can't set sparse flag on \"%ls\"", current_path(ctx)); return WIMLIB_ERR_SET_ATTRIBUTES; } /* Try to enable short name support on the target volume. If successful, return * true. If unsuccessful, issue a warning and return false. */ static bool try_to_enable_short_names(const wchar_t *volume) { HANDLE h; FILE_FS_PERSISTENT_VOLUME_INFORMATION info; BOOL bret; DWORD bytesReturned; h = CreateFile(volume, GENERIC_WRITE, FILE_SHARE_VALID_FLAGS, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) goto fail; info.VolumeFlags = 0; info.FlagMask = PERSISTENT_VOLUME_STATE_SHORT_NAME_CREATION_DISABLED; info.Version = 1; info.Reserved = 0; bret = DeviceIoControl(h, FSCTL_SET_PERSISTENT_VOLUME_STATE, &info, sizeof(info), NULL, 0, &bytesReturned, NULL); CloseHandle(h); if (!bret) goto fail; return true; fail: win32_warning(GetLastError(), L"Failed to enable short name support on %ls", volume + 4); return false; } static NTSTATUS remove_conflicting_short_name(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx) { wchar_t *name; wchar_t *end; NTSTATUS status; HANDLE h; size_t bufsize = offsetof(FILE_NAME_INFORMATION, FileName) + (13 * sizeof(wchar_t)); u8 buf[bufsize] _aligned_attribute(8); bool retried = false; FILE_NAME_INFORMATION *info = (FILE_NAME_INFORMATION *)buf; memset(buf, 0, bufsize); /* Build the path with the short name. */ name = &ctx->pathbuf.Buffer[ctx->pathbuf.Length / sizeof(wchar_t)]; while (name != ctx->pathbuf.Buffer && *(name - 1) != L'\\') name--; end = mempcpy(name, dentry->d_short_name, dentry->d_short_name_nbytes); ctx->pathbuf.Length = ((u8 *)end - (u8 *)ctx->pathbuf.Buffer); /* Open the conflicting file (by short name). */ status = NtOpenFile(&h, GENERIC_WRITE | DELETE, &ctx->attr, &ctx->iosb, FILE_SHARE_VALID_FLAGS, FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT); if (!NT_SUCCESS(status)) { winnt_warning(status, L"Can't open \"%ls\"", current_path(ctx)); goto out; } #if 0 WARNING("Overriding conflicting short name; path=\"%ls\"", current_path(ctx)); #endif /* Try to remove the short name on the conflicting file. */ retry: status = NtSetInformationFile(h, &ctx->iosb, info, bufsize, FileShortNameInformation); if (status == STATUS_INVALID_PARAMETER && !retried) { /* Microsoft forgot to make it possible to remove short names * until Windows 7. Oops. Use a random short name instead. */ get_random_alnum_chars(info->FileName, 8); wcscpy(&info->FileName[8], L".WLB"); info->FileNameLength = 12 * sizeof(wchar_t); retried = true; goto retry; } NtClose(h); out: build_extraction_path(dentry, ctx); return status; } /* Set the short name on the open file @h which has been created at the location * indicated by @dentry. * * Note that this may add, change, or remove the short name. * * @h must be opened with DELETE access. * * Returns 0 or WIMLIB_ERR_SET_SHORT_NAME. The latter only happens in * STRICT_SHORT_NAMES mode. */ static int set_short_name(HANDLE h, const struct wim_dentry *dentry, struct win32_apply_ctx *ctx) { if (!ctx->common.supported_features.short_names) return 0; /* * Note: The size of the FILE_NAME_INFORMATION buffer must be such that * FileName contains at least 2 wide characters (4 bytes). Otherwise, * NtSetInformationFile() will return STATUS_INFO_LENGTH_MISMATCH. This * is despite the fact that FileNameLength can validly be 0 or 2 bytes, * with the former case being removing the existing short name if * present, rather than setting one. * * The null terminator is seemingly optional, but to be safe we include * space for it and zero all unused space. */ size_t bufsize = offsetof(FILE_NAME_INFORMATION, FileName) + max(dentry->d_short_name_nbytes, sizeof(wchar_t)) + sizeof(wchar_t); u8 buf[bufsize] _aligned_attribute(8); FILE_NAME_INFORMATION *info = (FILE_NAME_INFORMATION *)buf; NTSTATUS status; bool tried_to_remove_existing = false; memset(buf, 0, bufsize); info->FileNameLength = dentry->d_short_name_nbytes; memcpy(info->FileName, dentry->d_short_name, dentry->d_short_name_nbytes); retry: status = NtSetInformationFile(h, &ctx->iosb, info, bufsize, FileShortNameInformation); if (NT_SUCCESS(status)) return 0; if (status == STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME) { if (dentry->d_short_name_nbytes == 0) return 0; if (!ctx->tried_to_enable_short_names) { wchar_t volume[7]; int ret; ctx->tried_to_enable_short_names = true; ret = win32_get_drive_path(ctx->common.target, volume); if (ret) return ret; if (try_to_enable_short_names(volume)) goto retry; } } /* * Short names can conflict in several cases: * * - a file being extracted has a short name conflicting with an * existing file * * - a file being extracted has a short name conflicting with another * file being extracted (possible, but shouldn't happen) * * - a file being extracted has a short name that conflicts with the * automatically generated short name of a file we previously * extracted, but failed to set the short name for. Sounds unlikely, * but this actually does happen fairly often on versions of Windows * prior to Windows 7 because they do not support removing short names * from files. */ if (unlikely(status == STATUS_OBJECT_NAME_COLLISION) && dentry->d_short_name_nbytes && !tried_to_remove_existing) { tried_to_remove_existing = true; status = remove_conflicting_short_name(dentry, ctx); if (NT_SUCCESS(status)) goto retry; } /* By default, failure to set short names is not an error (since short * names aren't too important anymore...). */ if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES)) { if (dentry->d_short_name_nbytes) ctx->num_set_short_name_failures++; else ctx->num_remove_short_name_failures++; return 0; } winnt_error(status, L"Can't set short name on \"%ls\"", current_path(ctx)); return WIMLIB_ERR_SET_SHORT_NAME; } /* * A wrapper around NtCreateFile() to make it slightly more usable... * This uses the path currently constructed in ctx->pathbuf. * * Also, we always specify SYNCHRONIZE access, FILE_OPEN_FOR_BACKUP_INTENT, and * FILE_OPEN_REPARSE_POINT. */ static NTSTATUS do_create_file(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, PLARGE_INTEGER AllocationSize, ULONG FileAttributes, ULONG CreateDisposition, ULONG CreateOptions, struct win32_apply_ctx *ctx) { return NtCreateFile(FileHandle, DesiredAccess | SYNCHRONIZE, &ctx->attr, &ctx->iosb, AllocationSize, FileAttributes, FILE_SHARE_VALID_FLAGS, CreateDisposition, CreateOptions | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT, NULL, 0); } /* Like do_create_file(), but builds the extraction path of the @dentry first. */ static NTSTATUS create_file(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, PLARGE_INTEGER AllocationSize, ULONG FileAttributes, ULONG CreateDisposition, ULONG CreateOptions, const struct wim_dentry *dentry, struct win32_apply_ctx *ctx) { build_extraction_path(dentry, ctx); return do_create_file(FileHandle, DesiredAccess, AllocationSize, FileAttributes, CreateDisposition, CreateOptions, ctx); } static int delete_file_or_stream(struct win32_apply_ctx *ctx) { NTSTATUS status; HANDLE h; ULONG perms = DELETE; ULONG flags = FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE; /* First try opening the file with FILE_DELETE_ON_CLOSE. In most cases, * all we have to do is that plus close the file handle. */ retry: status = do_create_file(&h, perms, NULL, 0, FILE_OPEN, flags, ctx); if (unlikely(status == STATUS_CANNOT_DELETE)) { /* This error occurs for files with FILE_ATTRIBUTE_READONLY set. * Try an alternate approach: first open the file without * FILE_DELETE_ON_CLOSE, then reset the file attributes, then * set the "delete" disposition on the handle. */ if (flags & FILE_DELETE_ON_CLOSE) { flags &= ~FILE_DELETE_ON_CLOSE; perms |= FILE_WRITE_ATTRIBUTES; goto retry; } } if (unlikely(!NT_SUCCESS(status))) { winnt_error(status, L"Can't open \"%ls\" for deletion " "(perms=%x, flags=%x)", current_path(ctx), perms, flags); return WIMLIB_ERR_OPEN; } if (unlikely(!(flags & FILE_DELETE_ON_CLOSE))) { FILE_BASIC_INFORMATION basic_info = { .FileAttributes = FILE_ATTRIBUTE_NORMAL }; status = NtSetInformationFile(h, &ctx->iosb, &basic_info, sizeof(basic_info), FileBasicInformation); if (!NT_SUCCESS(status)) { winnt_error(status, L"Can't reset attributes of \"%ls\" " "to prepare for deletion", current_path(ctx)); NtClose(h); return WIMLIB_ERR_SET_ATTRIBUTES; } FILE_DISPOSITION_INFORMATION disp_info = { .DoDeleteFile = TRUE }; status = NtSetInformationFile(h, &ctx->iosb, &disp_info, sizeof(disp_info), FileDispositionInformation); if (!NT_SUCCESS(status)) { winnt_error(status, L"Can't set delete-on-close " "disposition on \"%ls\"", current_path(ctx)); NtClose(h); return WIMLIB_ERR_SET_ATTRIBUTES; } } status = NtClose(h); if (unlikely(!NT_SUCCESS(status))) { winnt_error(status, L"Error closing \"%ls\" after setting " "delete-on-close disposition", current_path(ctx)); return WIMLIB_ERR_OPEN; } return 0; } /* * Create a nondirectory file or named data stream at the current path, * superseding any that already exists at that path. If successful, return an * open handle to the file or named data stream with the requested permissions. */ static int supersede_file_or_stream(struct win32_apply_ctx *ctx, DWORD perms, HANDLE *h_ret) { NTSTATUS status; bool retried = false; /* FILE_ATTRIBUTE_SYSTEM is needed to ensure that * FILE_ATTRIBUTE_ENCRYPTED doesn't get set before we want it to be. */ retry: status = do_create_file(h_ret, perms, NULL, FILE_ATTRIBUTE_SYSTEM, FILE_CREATE, FILE_NON_DIRECTORY_FILE, ctx); if (likely(NT_SUCCESS(status))) return 0; /* STATUS_OBJECT_NAME_COLLISION means that the file or stream already * exists. Delete the existing file or stream, then try again. * * Note: we don't use FILE_OVERWRITE_IF or FILE_SUPERSEDE because of * problems with certain file attributes, especially * FILE_ATTRIBUTE_ENCRYPTED. FILE_SUPERSEDE is also broken in the * Windows PE ramdisk. */ if (status == STATUS_OBJECT_NAME_COLLISION && !retried) { int ret = delete_file_or_stream(ctx); if (ret) return ret; retried = true; goto retry; } winnt_error(status, L"Can't create \"%ls\"", current_path(ctx)); return WIMLIB_ERR_OPEN; } /* Set the reparse point @rpbuf of length @rpbuflen on the extracted file * corresponding to the WIM dentry @dentry. */ static int do_set_reparse_point(const struct wim_dentry *dentry, const struct reparse_buffer_disk *rpbuf, u16 rpbuflen, struct win32_apply_ctx *ctx) { NTSTATUS status; HANDLE h; status = create_file(&h, GENERIC_WRITE, NULL, 0, FILE_OPEN, 0, dentry, ctx); if (!NT_SUCCESS(status)) goto fail; status = winnt_fsctl(h, FSCTL_SET_REPARSE_POINT, rpbuf, rpbuflen, NULL, 0, NULL); NtClose(h); if (NT_SUCCESS(status)) return 0; /* On Windows, by default only the Administrator can create symbolic * links for some reason. By default we just issue a warning if this * appears to be the problem. Use WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS * to get a hard error. */ if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS) && (status == STATUS_PRIVILEGE_NOT_HELD || status == STATUS_ACCESS_DENIED) && (dentry->d_inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK || dentry->d_inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT)) { WARNING("Can't create symbolic link \"%ls\"! \n" " (Need Administrator rights, or at least " "the\n" " SeCreateSymbolicLink privilege.)", current_path(ctx)); return 0; } fail: winnt_error(status, L"Can't set reparse data on \"%ls\"", current_path(ctx)); return WIMLIB_ERR_SET_REPARSE_DATA; } /* * Create empty named data streams and potentially a reparse point for the * specified file, if any. * * Since these won't have blob descriptors, they won't show up in the call to * extract_blob_list(). Hence the need for the special case. */ static int create_empty_streams(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx) { const struct wim_inode *inode = dentry->d_inode; int ret; for (unsigned i = 0; i < inode->i_num_streams; i++) { const struct wim_inode_stream *strm = &inode->i_streams[i]; if (stream_blob_resolved(strm) != NULL) continue; if (strm->stream_type == STREAM_TYPE_REPARSE_POINT && ctx->common.supported_features.reparse_points) { u8 buf[REPARSE_DATA_OFFSET] _aligned_attribute(8); struct reparse_buffer_disk *rpbuf = (struct reparse_buffer_disk *)buf; complete_reparse_point(rpbuf, inode, 0); ret = do_set_reparse_point(dentry, rpbuf, REPARSE_DATA_OFFSET, ctx); if (ret) return ret; } else if (stream_is_named_data_stream(strm) && ctx->common.supported_features.named_data_streams) { HANDLE h; build_extraction_path_with_ads(dentry, ctx, strm->stream_name, utf16le_len_chars(strm->stream_name)); /* * Note: do not request any permissions on the handle. * Otherwise, we may encounter a Windows bug where the * parent directory DACL denies read access to the new * named data stream, even when using backup semantics! */ ret = supersede_file_or_stream(ctx, 0, &h); build_extraction_path(dentry, ctx); if (ret) return ret; NtClose(h); } } return 0; } /* * Creates the directory named by @dentry, or uses an existing directory at that * location. If necessary, sets the short name and/or fixes compression and * encryption attributes. * * Returns 0, WIMLIB_ERR_MKDIR, or WIMLIB_ERR_SET_SHORT_NAME. */ static int create_directory(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx) { DWORD perms; NTSTATUS status; HANDLE h; int ret; /* DELETE is needed for set_short_name(); GENERIC_READ and GENERIC_WRITE * are needed for adjust_compression_attribute(). */ perms = GENERIC_READ | GENERIC_WRITE; if (!dentry_is_root(dentry)) perms |= DELETE; /* FILE_ATTRIBUTE_SYSTEM is needed to ensure that * FILE_ATTRIBUTE_ENCRYPTED doesn't get set before we want it to be. */ status = create_file(&h, perms, NULL, FILE_ATTRIBUTE_SYSTEM, FILE_OPEN_IF, FILE_DIRECTORY_FILE, dentry, ctx); if (unlikely(!NT_SUCCESS(status))) { const wchar_t *path = current_path(ctx); winnt_error(status, L"Can't create directory \"%ls\"", path); /* Check for known issue with WindowsApps directory. */ if (status == STATUS_ACCESS_DENIED && (wcsstr(path, L"\\WindowsApps\\") || wcsstr(path, L"\\InfusedApps\\"))) { ERROR( "You seem to be trying to extract files to the WindowsApps directory.\n" " Windows 8.1 and later use new file permissions in this directory that\n" " cannot be overridden, even by backup/restore programs. To extract your\n" " files anyway, you need to choose a different target directory, delete\n" " the WindowsApps directory entirely, reformat the volume, do the\n" " extraction from a non-broken operating system such as Windows 7 or\n" " Linux, or wait for Microsoft to fix the design flaw in their operating\n" " system. This is *not* a bug in wimlib. See this thread for more\n" " information: https://wimlib.net/forums/viewtopic.php?f=1&t=261"); } return WIMLIB_ERR_MKDIR; } if (ctx->iosb.Information == FILE_OPENED) { /* If we opened an existing directory, try to clear its file * attributes. As far as I know, this only actually makes a * difference in the case where a FILE_ATTRIBUTE_READONLY * directory has a named data stream which needs to be * extracted. You cannot create a named data stream of such a * directory, even though this contradicts Microsoft's * documentation for FILE_ATTRIBUTE_READONLY which states it is * not honored for directories! */ if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES)) { FILE_BASIC_INFORMATION basic_info = { .FileAttributes = FILE_ATTRIBUTE_NORMAL }; NtSetInformationFile(h, &ctx->iosb, &basic_info, sizeof(basic_info), FileBasicInformation); } } if (!dentry_is_root(dentry)) { ret = set_short_name(h, dentry, ctx); if (ret) goto out; } ret = adjust_compression_attribute(h, dentry, ctx); out: NtClose(h); return ret; } /* * Create all the directories being extracted, other than the target directory * itself. * * Note: we don't honor directory hard links. However, we don't allow them to * exist in WIM images anyway (see inode_fixup.c). */ static int create_directories(struct list_head *dentry_list, struct win32_apply_ctx *ctx) { const struct wim_dentry *dentry; int ret; list_for_each_entry(dentry, dentry_list, d_extraction_list_node) { if (!(dentry->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)) continue; /* Note: Here we include files with * FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_REPARSE_POINT, but we * wait until later to actually set the reparse data. */ ret = create_directory(dentry, ctx); if (!ret) ret = create_empty_streams(dentry, ctx); ret = check_apply_error(dentry, ctx, ret); if (ret) return ret; ret = report_file_created(&ctx->common); if (ret) return ret; } return 0; } /* * Creates the nondirectory file named by @dentry. * * On success, returns an open handle to the file in @h_ret, with GENERIC_READ, * GENERIC_WRITE, and DELETE access. Also, the path to the file will be saved * in ctx->pathbuf. On failure, returns an error code. */ static int create_nondirectory_inode(HANDLE *h_ret, const struct wim_dentry *dentry, struct win32_apply_ctx *ctx) { int ret; HANDLE h; build_extraction_path(dentry, ctx); ret = supersede_file_or_stream(ctx, GENERIC_READ | GENERIC_WRITE | DELETE, &h); if (ret) goto out; ret = adjust_compression_attribute(h, dentry, ctx); if (ret) goto out_close; if (need_sparse_flag(dentry->d_inode, ctx)) { ret = set_sparse_flag(h, ctx); if (ret) goto out_close; } ret = create_empty_streams(dentry, ctx); if (ret) goto out_close; *h_ret = h; return 0; out_close: NtClose(h); out: return ret; } /* Creates a hard link at the location named by @dentry to the file represented * by the open handle @h. Or, if the target volume does not support hard links, * create a separate file instead. */ static int create_link(HANDLE h, const struct wim_dentry *dentry, struct win32_apply_ctx *ctx) { if (ctx->common.supported_features.hard_links) { build_extraction_path(dentry, ctx); size_t bufsize = offsetof(FILE_LINK_INFORMATION, FileName) + ctx->pathbuf.Length + sizeof(wchar_t); u8 buf[bufsize] _aligned_attribute(8); FILE_LINK_INFORMATION *info = (FILE_LINK_INFORMATION *)buf; NTSTATUS status; info->ReplaceIfExists = TRUE; info->RootDirectory = ctx->attr.RootDirectory; info->FileNameLength = ctx->pathbuf.Length; memcpy(info->FileName, ctx->pathbuf.Buffer, ctx->pathbuf.Length); info->FileName[info->FileNameLength / 2] = L'\0'; /* Note: the null terminator isn't actually necessary, * but if you don't add the extra character, you get * STATUS_INFO_LENGTH_MISMATCH when FileNameLength * happens to be 2 */ status = NtSetInformationFile(h, &ctx->iosb, info, bufsize, FileLinkInformation); if (NT_SUCCESS(status)) return 0; winnt_error(status, L"Failed to create link \"%ls\"", current_path(ctx)); return WIMLIB_ERR_LINK; } else { HANDLE h2; int ret; ret = create_nondirectory_inode(&h2, dentry, ctx); if (ret) return ret; NtClose(h2); return 0; } } /* Given an inode (represented by the open handle @h) for which one link has * been created (named by @first_dentry), create the other links. * * Or, if the target volume does not support hard links, create separate files. * * Note: This uses ctx->pathbuf and does not reset it. */ static int create_links(HANDLE h, const struct wim_dentry *first_dentry, struct win32_apply_ctx *ctx) { const struct wim_inode *inode = first_dentry->d_inode; const struct wim_dentry *dentry; int ret; inode_for_each_extraction_alias(dentry, inode) { if (dentry != first_dentry) { ret = create_link(h, dentry, ctx); if (ret) return ret; } } return 0; } /* Create a nondirectory file, including all links. */ static int create_nondirectory(struct wim_inode *inode, struct win32_apply_ctx *ctx) { struct wim_dentry *first_dentry; HANDLE h; int ret; first_dentry = first_extraction_alias(inode); /* Create first link. */ ret = create_nondirectory_inode(&h, first_dentry, ctx); if (ret) return ret; /* Set short name. */ ret = set_short_name(h, first_dentry, ctx); /* Create additional links, OR if hard links are not supported just * create more files. */ if (!ret) ret = create_links(h, first_dentry, ctx); /* "WIMBoot" extraction: set external backing by the WIM file if needed. */ if (!ret && unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT)) ret = set_backed_from_wim(h, inode, ctx); NtClose(h); return ret; } /* Create all the nondirectory files being extracted, including all aliases * (hard links). */ static int create_nondirectories(struct list_head *dentry_list, struct win32_apply_ctx *ctx) { struct wim_dentry *dentry; struct wim_inode *inode; int ret; list_for_each_entry(dentry, dentry_list, d_extraction_list_node) { inode = dentry->d_inode; if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) continue; /* Call create_nondirectory() only once per inode */ if (dentry == inode_first_extraction_dentry(inode)) { ret = create_nondirectory(inode, ctx); ret = check_apply_error(dentry, ctx, ret); if (ret) return ret; } ret = report_file_created(&ctx->common); if (ret) return ret; } return 0; } static void close_handles(struct win32_apply_ctx *ctx) { for (unsigned i = 0; i < ctx->num_open_handles; i++) NtClose(ctx->open_handles[i]); } /* Prepare to read the next blob, which has size @blob_size, into an in-memory * buffer. */ static bool prepare_data_buffer(struct win32_apply_ctx *ctx, u64 blob_size) { if (blob_size > ctx->data_buffer_size) { /* Larger buffer needed. */ void *new_buffer; if ((size_t)blob_size != blob_size) return false; new_buffer = REALLOC(ctx->data_buffer, blob_size); if (!new_buffer) return false; ctx->data_buffer = new_buffer; ctx->data_buffer_size = blob_size; } /* On the first call this changes data_buffer_ptr from NULL, which tells * extract_chunk() that the data buffer needs to be filled while reading * the stream data. */ ctx->data_buffer_ptr = ctx->data_buffer; return true; } static int begin_extract_blob_instance(const struct blob_descriptor *blob, struct wim_dentry *dentry, const struct wim_inode_stream *strm, struct win32_apply_ctx *ctx) { HANDLE h; NTSTATUS status; if (unlikely(strm->stream_type == STREAM_TYPE_REPARSE_POINT)) { /* We can't write the reparse point stream directly; we must set * it with FSCTL_SET_REPARSE_POINT, which requires that all the * data be available. So, stage the data in a buffer. */ if (!prepare_data_buffer(ctx, blob->size)) return WIMLIB_ERR_NOMEM; list_add_tail(&dentry->d_tmp_list, &ctx->reparse_dentries); return 0; } if (unlikely(strm->stream_type == STREAM_TYPE_EFSRPC_RAW_DATA)) { /* We can't write encrypted files directly; we must use * WriteEncryptedFileRaw(), which requires providing the data * through a callback function. This can't easily be combined * with our own callback-based approach. * * The current workaround is to simply read the blob into memory * and write the encrypted file from that. * * TODO: This isn't sufficient for extremely large encrypted * files. Perhaps we should create an extra thread to write * such files... */ if (!prepare_data_buffer(ctx, blob->size)) return WIMLIB_ERR_NOMEM; list_add_tail(&dentry->d_tmp_list, &ctx->encrypted_dentries); return 0; } /* It's a data stream (may be unnamed or named). */ wimlib_assert(strm->stream_type == STREAM_TYPE_DATA); if (ctx->num_open_handles == MAX_OPEN_FILES) { /* XXX: Fix this. But because of the checks in * extract_blob_list(), this can now only happen on a filesystem * that does not support hard links. */ ERROR("Can't extract data: too many open files!"); return WIMLIB_ERR_UNSUPPORTED; } if (unlikely(stream_is_named(strm))) { build_extraction_path_with_ads(dentry, ctx, strm->stream_name, utf16le_len_chars(strm->stream_name)); } else { build_extraction_path(dentry, ctx); } /* Open a new handle */ status = do_create_file(&h, FILE_WRITE_DATA | SYNCHRONIZE, NULL, 0, FILE_OPEN_IF, FILE_SEQUENTIAL_ONLY | FILE_SYNCHRONOUS_IO_NONALERT, ctx); if (!NT_SUCCESS(status)) { winnt_error(status, L"Can't open \"%ls\" for writing", current_path(ctx)); return WIMLIB_ERR_OPEN; } ctx->is_sparse_stream[ctx->num_open_handles] = false; if (need_sparse_flag(dentry->d_inode, ctx)) { /* If the stream is unnamed, then the sparse flag was already * set when the file was created. But if the stream is named, * then we need to set the sparse flag here. */ if (unlikely(stream_is_named(strm))) { int ret = set_sparse_flag(h, ctx); if (ret) { NtClose(h); return ret; } } ctx->is_sparse_stream[ctx->num_open_handles] = true; ctx->any_sparse_streams = true; } else { /* Allocate space for the data. */ FILE_ALLOCATION_INFORMATION info = { .AllocationSize = { .QuadPart = blob->size }}; NtSetInformationFile(h, &ctx->iosb, &info, sizeof(info), FileAllocationInformation); } ctx->open_handles[ctx->num_open_handles++] = h; return 0; } /* Given a Windows NT namespace path, such as \??\e:\Windows\System32, return a * pointer to the suffix of the path that begins with the device directly, such * as e:\Windows\System32. */ static const wchar_t * skip_nt_toplevel_component(const wchar_t *path, size_t path_nchars) { static const wchar_t * const dirs[] = { L"\\??\\", L"\\DosDevices\\", L"\\Device\\", }; const wchar_t * const end = path + path_nchars; for (size_t i = 0; i < ARRAY_LEN(dirs); i++) { size_t len = wcslen(dirs[i]); if (len <= (end - path) && !wmemcmp(path, dirs[i], len)) { path += len; while (path != end && *path == L'\\') path++; return path; } } return path; } /* * Given a Windows NT namespace path, such as \??\e:\Windows\System32, return a * pointer to the suffix of the path that is device-relative but possibly with * leading slashes, such as \Windows\System32. * * The path has an explicit length and is not necessarily null terminated. */ static const wchar_t * get_device_relative_path(const wchar_t *path, size_t path_nchars) { const wchar_t * const orig_path = path; const wchar_t * const end = path + path_nchars; path = skip_nt_toplevel_component(path, path_nchars); if (path == orig_path) return orig_path; while (path != end && *path != L'\\') path++; return path; } /* * Given a reparse point buffer for an inode for which the absolute link target * was relativized when it was archived, de-relative the link target to be * consistent with the actual extraction location. */ static void try_rpfix(struct reparse_buffer_disk *rpbuf, u16 *rpbuflen_p, struct win32_apply_ctx *ctx) { struct link_reparse_point link; size_t orig_subst_name_nchars; const wchar_t *relpath; size_t relpath_nchars; size_t target_ntpath_nchars; size_t fixed_subst_name_nchars; const wchar_t *fixed_print_name; size_t fixed_print_name_nchars; /* Do nothing if the reparse data is invalid. */ if (parse_link_reparse_point(rpbuf, *rpbuflen_p, &link)) return; /* Do nothing if the reparse point is a relative symbolic link. */ if (link_is_relative_symlink(&link)) return; /* Build the new substitute name from the NT namespace path to the * target directory, then a path separator, then the "device relative" * part of the old substitute name. */ orig_subst_name_nchars = link.substitute_name_nbytes / sizeof(wchar_t); relpath = get_device_relative_path(link.substitute_name, orig_subst_name_nchars); relpath_nchars = orig_subst_name_nchars - (relpath - link.substitute_name); target_ntpath_nchars = ctx->target_ntpath.Length / sizeof(wchar_t); /* If the target directory is a filesystem root, such as \??\C:\, then * it already will have a trailing slash. Don't include this slash if * we are already adding slashes via 'relpath'. This prevents an extra * slash from being generated each time the link is extracted. And * unlike on UNIX, the number of slashes in paths on Windows can be * significant; Windows won't understand the link target if it contains * too many slashes. */ if (target_ntpath_nchars > 0 && relpath_nchars > 0 && ctx->target_ntpath.Buffer[target_ntpath_nchars - 1] == L'\\') target_ntpath_nchars--; /* Also remove extra slashes from the beginning of 'relpath'. Normally * this isn't needed, but this is here to make the extra slash(es) added * by wimlib pre-v1.9.1 get removed automatically. */ while (relpath_nchars >= 2 && relpath[0] == L'\\' && relpath[1] == L'\\') { relpath++; relpath_nchars--; } fixed_subst_name_nchars = target_ntpath_nchars + relpath_nchars; wchar_t fixed_subst_name[fixed_subst_name_nchars]; wmemcpy(fixed_subst_name, ctx->target_ntpath.Buffer, target_ntpath_nchars); wmemcpy(&fixed_subst_name[target_ntpath_nchars], relpath, relpath_nchars); /* Doesn't need to be null-terminated. */ /* Print name should be Win32, but not all NT names can even be * translated to Win32 names. But we can at least delete the top-level * directory, such as \??\, and this will have the expected result in * the usual case. */ fixed_print_name = skip_nt_toplevel_component(fixed_subst_name, fixed_subst_name_nchars); fixed_print_name_nchars = fixed_subst_name_nchars - (fixed_print_name - fixed_subst_name); link.substitute_name = fixed_subst_name; link.substitute_name_nbytes = fixed_subst_name_nchars * sizeof(wchar_t); link.print_name = (wchar_t *)fixed_print_name; link.print_name_nbytes = fixed_print_name_nchars * sizeof(wchar_t); make_link_reparse_point(&link, rpbuf, rpbuflen_p); } /* Sets the reparse point on the specified file. This handles "fixing" the * targets of absolute symbolic links and junctions if WIMLIB_EXTRACT_FLAG_RPFIX * was specified. */ static int set_reparse_point(const struct wim_dentry *dentry, const struct reparse_buffer_disk *rpbuf, u16 rpbuflen, struct win32_apply_ctx *ctx) { if ((ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX) && !(dentry->d_inode->i_rp_flags & WIM_RP_FLAG_NOT_FIXED)) { memcpy(&ctx->rpfixbuf, rpbuf, rpbuflen); try_rpfix(&ctx->rpfixbuf, &rpbuflen, ctx); rpbuf = &ctx->rpfixbuf; } return do_set_reparse_point(dentry, rpbuf, rpbuflen, ctx); } /* Import the next block of raw encrypted data */ static DWORD WINAPI import_encrypted_data(PBYTE pbData, PVOID pvCallbackContext, PULONG Length) { struct win32_apply_ctx *ctx = pvCallbackContext; ULONG copy_len; copy_len = min(ctx->encrypted_size - ctx->encrypted_offset, *Length); memcpy(pbData, &ctx->data_buffer[ctx->encrypted_offset], copy_len); ctx->encrypted_offset += copy_len; *Length = copy_len; return ERROR_SUCCESS; } /* * Write the raw encrypted data to the already-created file (or directory) * corresponding to @dentry. * * The raw encrypted data is provided in ctx->data_buffer, and its size is * ctx->encrypted_size. * * This function may close the target directory, in which case the caller needs * to re-open it if needed. */ static int extract_encrypted_file(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx) { void *rawctx; DWORD err; ULONG flags; bool retried; /* Temporarily build a Win32 path for OpenEncryptedFileRaw() */ build_win32_extraction_path(dentry, ctx); flags = CREATE_FOR_IMPORT | OVERWRITE_HIDDEN; if (dentry->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) flags |= CREATE_FOR_DIR; retried = false; retry: err = OpenEncryptedFileRaw(ctx->pathbuf.Buffer, flags, &rawctx); if (err == ERROR_SHARING_VIOLATION && !retried) { /* This can be caused by the handle we have open to the target * directory. Try closing it temporarily. */ close_target_directory(ctx); retried = true; goto retry; } /* Restore the NT namespace path */ build_extraction_path(dentry, ctx); if (err != ERROR_SUCCESS) { win32_error(err, L"Can't open \"%ls\" for encrypted import", current_path(ctx)); return WIMLIB_ERR_OPEN; } ctx->encrypted_offset = 0; err = WriteEncryptedFileRaw(import_encrypted_data, ctx, rawctx); CloseEncryptedFileRaw(rawctx); if (err != ERROR_SUCCESS) { win32_error(err, L"Can't import encrypted file \"%ls\"", current_path(ctx)); return WIMLIB_ERR_WRITE; } return 0; } /* Called when starting to read a blob for extraction */ static int win32_begin_extract_blob(struct blob_descriptor *blob, void *_ctx) { struct win32_apply_ctx *ctx = _ctx; const struct blob_extraction_target *targets = blob_extraction_targets(blob); int ret; ctx->num_open_handles = 0; ctx->data_buffer_ptr = NULL; ctx->any_sparse_streams = false; INIT_LIST_HEAD(&ctx->reparse_dentries); INIT_LIST_HEAD(&ctx->encrypted_dentries); for (u32 i = 0; i < blob->out_refcnt; i++) { const struct wim_inode *inode = targets[i].inode; const struct wim_inode_stream *strm = targets[i].stream; struct wim_dentry *dentry; /* A copy of the blob needs to be extracted to @inode. */ if (ctx->common.supported_features.hard_links) { dentry = inode_first_extraction_dentry(inode); ret = begin_extract_blob_instance(blob, dentry, strm, ctx); ret = check_apply_error(dentry, ctx, ret); if (ret) goto fail; } else { /* Hard links not supported. Extract the blob * separately to each alias of the inode. */ inode_for_each_extraction_alias(dentry, inode) { ret = begin_extract_blob_instance(blob, dentry, strm, ctx); ret = check_apply_error(dentry, ctx, ret); if (ret) goto fail; } } } return 0; fail: close_handles(ctx); return ret; } static int pwrite_to_handle(HANDLE h, const void *data, size_t size, u64 offset) { const void * const end = data + size; const void *p; IO_STATUS_BLOCK iosb; NTSTATUS status; for (p = data; p != end; p += iosb.Information, offset += iosb.Information) { LARGE_INTEGER offs = { .QuadPart = offset }; status = NtWriteFile(h, NULL, NULL, NULL, &iosb, (void *)p, min(INT32_MAX, end - p), &offs, NULL); if (!NT_SUCCESS(status)) { winnt_error(status, L"Error writing data to target volume"); return WIMLIB_ERR_WRITE; } } return 0; } /* Called when the next chunk of a blob has been read for extraction */ static int win32_extract_chunk(const struct blob_descriptor *blob, u64 offset, const void *chunk, size_t size, void *_ctx) { struct win32_apply_ctx *ctx = _ctx; const void * const end = chunk + size; const void *p; bool zeroes; size_t len; unsigned i; int ret; /* * For sparse streams, only write nonzero regions. This lets the * filesystem use holes to represent zero regions. */ for (p = chunk; p != end; p += len, offset += len) { zeroes = maybe_detect_sparse_region(p, end - p, &len, ctx->any_sparse_streams); for (i = 0; i < ctx->num_open_handles; i++) { if (!zeroes || !ctx->is_sparse_stream[i]) { ret = pwrite_to_handle(ctx->open_handles[i], p, len, offset); if (ret) return ret; } } } /* Copy the data chunk into the buffer (if needed) */ if (ctx->data_buffer_ptr) ctx->data_buffer_ptr = mempcpy(ctx->data_buffer_ptr, chunk, size); return 0; } static int get_system_compression_format(int extract_flags) { if (extract_flags & WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K) return FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS4K; if (extract_flags & WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K) return FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS8K; if (extract_flags & WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K) return FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS16K; return FILE_PROVIDER_COMPRESSION_FORMAT_LZX; } static const wchar_t * get_system_compression_format_string(int format) { switch (format) { case FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS4K: return L"XPRESS4K"; case FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS8K: return L"XPRESS8K"; case FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS16K: return L"XPRESS16K"; default: return L"LZX"; } } static NTSTATUS set_system_compression(HANDLE h, int format) { NTSTATUS status; struct { struct wof_external_info wof_info; struct file_provider_external_info file_info; } in = { .wof_info = { .version = WOF_CURRENT_VERSION, .provider = WOF_PROVIDER_FILE, }, .file_info = { .version = FILE_PROVIDER_CURRENT_VERSION, .compression_format = format, }, }; /* We intentionally use NtFsControlFile() rather than DeviceIoControl() * here because the "compressing this object would not save space" * status code does not map to a valid Win32 error code on older * versions of Windows (before Windows 10?). This can be a problem if * the WOFADK driver is being used rather than the regular WOF, since * WOFADK can be used on older versions of Windows. */ status = winnt_fsctl(h, FSCTL_SET_EXTERNAL_BACKING, &in, sizeof(in), NULL, 0, NULL); if (status == 0xC000046F) /* "Compressing this object would not save space." */ return STATUS_SUCCESS; return status; } /* Hard-coded list of files which the Windows bootloader may need to access * before the WOF driver has been loaded. */ static const wchar_t * const bootloader_pattern_strings[] = { L"*winload.*", L"*winresume.*", L"\\Windows\\AppPatch\\drvmain.sdb", L"\\Windows\\Boot\\DVD\\*", L"\\Windows\\Boot\\EFI\\*", L"\\Windows\\bootstat.dat", L"\\Windows\\Fonts\\vgaoem.fon", L"\\Windows\\Fonts\\vgasys.fon", L"\\Windows\\INF\\errata.inf", L"\\Windows\\System32\\config\\*", L"\\Windows\\System32\\ntkrnlpa.exe", L"\\Windows\\System32\\ntoskrnl.exe", L"\\Windows\\System32\\bootvid.dll", L"\\Windows\\System32\\ci.dll", L"\\Windows\\System32\\hal*.dll", L"\\Windows\\System32\\mcupdate_AuthenticAMD.dll", L"\\Windows\\System32\\mcupdate_GenuineIntel.dll", L"\\Windows\\System32\\pshed.dll", L"\\Windows\\System32\\apisetschema.dll", L"\\Windows\\System32\\api-ms-win*.dll", L"\\Windows\\System32\\ext-ms-win*.dll", L"\\Windows\\System32\\KernelBase.dll", L"\\Windows\\System32\\drivers\\*.sys", L"\\Windows\\System32\\*.nls", L"\\Windows\\System32\\kbd*.dll", L"\\Windows\\System32\\kd*.dll", L"\\Windows\\System32\\clfs.sys", L"\\Windows\\System32\\CodeIntegrity\\driver.stl", }; static const struct string_list bootloader_patterns = { .strings = (wchar_t **)bootloader_pattern_strings, .num_strings = ARRAY_LEN(bootloader_pattern_strings), }; static NTSTATUS set_system_compression_on_inode(struct wim_inode *inode, int format, struct win32_apply_ctx *ctx) { bool retried = false; NTSTATUS status; HANDLE h; /* If it may be needed for compatibility with the Windows bootloader, * force this file to XPRESS4K or uncompressed format. The bootloader * of Windows 10 supports XPRESS4K only; older versions don't support * system compression at all. */ if (!is_image_windows_10_or_later(ctx) || format != FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS4K) { /* We need to check the patterns against every name of the * inode, in case any of them match. */ struct wim_dentry *dentry; inode_for_each_extraction_alias(dentry, inode) { bool incompatible; bool warned; if (calculate_dentry_full_path(dentry)) { ERROR("Unable to compute file path!"); return STATUS_NO_MEMORY; } incompatible = match_pattern_list(dentry->d_full_path, &bootloader_patterns, MATCH_RECURSIVELY); FREE(dentry->d_full_path); dentry->d_full_path = NULL; if (!incompatible) continue; warned = (ctx->num_system_compression_exclusions++ > 0); if (is_image_windows_10_or_later(ctx)) { /* Force to XPRESS4K */ if (!warned) { WARNING("For compatibility with the " "Windows bootloader, some " "files are being\n" " compacted " "using the XPRESS4K format " "instead of the %"TS" format\n" " you requested.", get_system_compression_format_string(format)); } format = FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS4K; break; } else { /* Force to uncompressed */ if (!warned) { WARNING("For compatibility with the " "Windows bootloader, some " "files will not\n" " be compressed with" " system compression " "(\"compacted\")."); } return STATUS_SUCCESS; } } } /* Open the extracted file. */ status = create_file(&h, GENERIC_READ | GENERIC_WRITE, NULL, 0, FILE_OPEN, 0, inode_first_extraction_dentry(inode), ctx); if (!NT_SUCCESS(status)) return status; retry: /* Compress the file. If the attempt fails with "invalid device * request", then attach wof.sys (or wofadk.sys) and retry. */ status = set_system_compression(h, format); if (unlikely(status == STATUS_INVALID_DEVICE_REQUEST && !retried)) { wchar_t drive_path[7]; if (!win32_get_drive_path(ctx->common.target, drive_path) && win32_try_to_attach_wof(drive_path + 4)) { retried = true; goto retry; } } NtClose(h); return status; } /* * This function is called when doing a "compact-mode" extraction and we just * finished extracting a blob to one or more locations. For each location that * was the unnamed data stream of a file, this function compresses the * corresponding file using System Compression, if allowed. * * Note: we're doing the compression immediately after extracting the data * rather than during a separate compression pass. This way should be faster * since the operating system should still have the file's data cached. * * Note: we're having the operating system do the compression, which is not * ideal because wimlib could create the compressed data faster and more * efficiently (the compressed data format is identical to a WIM resource). But * we seemingly don't have a choice because WOF prevents applications from * creating its reparse points. */ static void handle_system_compression(struct blob_descriptor *blob, struct win32_apply_ctx *ctx) { const struct blob_extraction_target *targets = blob_extraction_targets(blob); const int format = get_system_compression_format(ctx->common.extract_flags); for (u32 i = 0; i < blob->out_refcnt; i++) { struct wim_inode *inode = targets[i].inode; struct wim_inode_stream *strm = targets[i].stream; NTSTATUS status; if (!stream_is_unnamed_data_stream(strm)) continue; if (will_externally_back_inode(inode, ctx, NULL, false) != 0) continue; status = set_system_compression_on_inode(inode, format, ctx); if (likely(NT_SUCCESS(status))) continue; if (status == STATUS_INVALID_DEVICE_REQUEST) { WARNING( "The request to compress the extracted files using System Compression\n" " will not be honored because the operating system or target volume\n" " does not support it. System Compression is only supported on\n" " Windows 10 and later, and only on NTFS volumes."); ctx->common.extract_flags &= ~COMPACT_FLAGS; return; } ctx->num_system_compression_failures++; if (ctx->num_system_compression_failures < 10) { winnt_warning(status, L"\"%ls\": Failed to compress " "extracted file using System Compression", current_path(ctx)); } else if (ctx->num_system_compression_failures == 10) { WARNING("Suppressing further warnings about " "System Compression failures."); } } } /* Called when a blob has been fully read for extraction */ static int win32_end_extract_blob(struct blob_descriptor *blob, int status, void *_ctx) { struct win32_apply_ctx *ctx = _ctx; int ret; const struct wim_dentry *dentry; /* Extend sparse streams to their final size. */ if (ctx->any_sparse_streams && !status) { for (unsigned i = 0; i < ctx->num_open_handles; i++) { FILE_END_OF_FILE_INFORMATION info = { .EndOfFile = { .QuadPart = blob->size } }; NTSTATUS ntstatus; if (!ctx->is_sparse_stream[i]) continue; ntstatus = NtSetInformationFile(ctx->open_handles[i], &ctx->iosb, &info, sizeof(info), FileEndOfFileInformation); if (!NT_SUCCESS(ntstatus)) { winnt_error(ntstatus, L"Error writing data to " "target volume (while extending)"); status = WIMLIB_ERR_WRITE; break; } } } close_handles(ctx); if (status) return status; if (unlikely(ctx->common.extract_flags & COMPACT_FLAGS)) handle_system_compression(blob, ctx); if (likely(!ctx->data_buffer_ptr)) return 0; if (!list_empty(&ctx->reparse_dentries)) { if (blob->size > REPARSE_DATA_MAX_SIZE) { dentry = list_first_entry(&ctx->reparse_dentries, struct wim_dentry, d_tmp_list); build_extraction_path(dentry, ctx); ERROR("Reparse data of \"%ls\" has size " "%"PRIu64" bytes (exceeds %u bytes)", current_path(ctx), blob->size, REPARSE_DATA_MAX_SIZE); ret = WIMLIB_ERR_INVALID_REPARSE_DATA; return check_apply_error(dentry, ctx, ret); } /* Reparse data */ memcpy(ctx->rpbuf.rpdata, ctx->data_buffer, blob->size); list_for_each_entry(dentry, &ctx->reparse_dentries, d_tmp_list) { /* Reparse point header */ complete_reparse_point(&ctx->rpbuf, dentry->d_inode, blob->size); ret = set_reparse_point(dentry, &ctx->rpbuf, REPARSE_DATA_OFFSET + blob->size, ctx); ret = check_apply_error(dentry, ctx, ret); if (ret) return ret; } } if (!list_empty(&ctx->encrypted_dentries)) { ctx->encrypted_size = blob->size; list_for_each_entry(dentry, &ctx->encrypted_dentries, d_tmp_list) { ret = extract_encrypted_file(dentry, ctx); ret = check_apply_error(dentry, ctx, ret); if (ret) return ret; /* Re-open the target directory if needed. */ ret = open_target_directory(ctx); if (ret) return ret; } } return 0; } /* Attributes that can't be set directly */ #define SPECIAL_ATTRIBUTES \ (FILE_ATTRIBUTE_REPARSE_POINT | \ FILE_ATTRIBUTE_DIRECTORY | \ FILE_ATTRIBUTE_ENCRYPTED | \ FILE_ATTRIBUTE_SPARSE_FILE | \ FILE_ATTRIBUTE_COMPRESSED) static void set_object_id(HANDLE h, const struct wim_inode *inode, struct win32_apply_ctx *ctx) { const void *object_id; u32 len; NTSTATUS status; if (!ctx->common.supported_features.object_ids) return; object_id = inode_get_object_id(inode, &len); if (likely(object_id == NULL)) /* No object ID? */ return; status = winnt_fsctl(h, FSCTL_SET_OBJECT_ID, object_id, len, NULL, 0, NULL); if (NT_SUCCESS(status)) return; /* Object IDs must be unique within the filesystem. A duplicate might * occur if an image containing object IDs is applied twice to the same * filesystem. Arguably, the user should be warned in this case; but * the reality seems to be that nothing important cares about object IDs * except the Distributed Link Tracking Service... so for now these * failures are just ignored. */ if (status == STATUS_DUPLICATE_NAME || status == STATUS_OBJECT_NAME_COLLISION) return; ctx->num_object_id_failures++; if (ctx->num_object_id_failures < 10) { winnt_warning(status, L"Can't set object ID on \"%ls\"", current_path(ctx)); } else if (ctx->num_object_id_failures == 10) { WARNING("Suppressing further warnings about failure to set " "object IDs."); } } static int set_xattrs(HANDLE h, const struct wim_inode *inode, struct win32_apply_ctx *ctx) { const void *entries, *entries_end; u32 len; const struct wim_xattr_entry *entry; size_t bufsize = 0; u8 _buf[1024] _aligned_attribute(4); u8 *buf = _buf; FILE_FULL_EA_INFORMATION *ea, *ea_prev; NTSTATUS status; int ret; if (!ctx->common.supported_features.xattrs) return 0; entries = inode_get_xattrs(inode, &len); if (likely(entries == NULL || len == 0)) /* No extended attributes? */ return 0; entries_end = entries + len; entry = entries; for (entry = entries; (void *)entry < entries_end; entry = xattr_entry_next(entry)) { if (!valid_xattr_entry(entry, entries_end - (void *)entry)) { ERROR("\"%"TS"\": extended attribute is corrupt or unsupported", inode_any_full_path(inode)); return WIMLIB_ERR_INVALID_XATTR; } bufsize += ALIGN(offsetof(FILE_FULL_EA_INFORMATION, EaName) + entry->name_len + 1 + le16_to_cpu(entry->value_len), 4); } if (unlikely(bufsize != (u32)bufsize)) { ERROR("\"%"TS"\": too many extended attributes to extract!", inode_any_full_path(inode)); return WIMLIB_ERR_INVALID_XATTR; } if (unlikely(bufsize > sizeof(_buf))) { buf = MALLOC(bufsize); if (!buf) return WIMLIB_ERR_NOMEM; } ea_prev = NULL; ea = (FILE_FULL_EA_INFORMATION *)buf; for (entry = entries; (void *)entry < entries_end; entry = xattr_entry_next(entry)) { u8 *p; if (ea_prev) ea_prev->NextEntryOffset = (u8 *)ea - (u8 *)ea_prev; ea->Flags = entry->flags; ea->EaNameLength = entry->name_len; ea->EaValueLength = le16_to_cpu(entry->value_len); p = mempcpy(ea->EaName, entry->name, ea->EaNameLength + 1 + ea->EaValueLength); while ((uintptr_t)p & 3) *p++ = 0; ea_prev = ea; ea = (FILE_FULL_EA_INFORMATION *)p; } ea_prev->NextEntryOffset = 0; wimlib_assert((u8 *)ea - buf == bufsize); status = NtSetEaFile(h, &ctx->iosb, buf, bufsize); if (unlikely(!NT_SUCCESS(status))) { if (status == STATUS_EAS_NOT_SUPPORTED) { /* This happens with Samba. */ WARNING("Filesystem advertised extended attribute (EA) support, but it doesn't\n" " work. EAs will not be extracted."); ctx->common.supported_features.xattrs = 0; } else if (status == STATUS_INVALID_EA_NAME) { ctx->num_xattr_failures++; if (ctx->num_xattr_failures < 5) { winnt_warning(status, L"Can't set extended attributes on \"%ls\"", current_path(ctx)); } else if (ctx->num_xattr_failures == 5) { WARNING("Suppressing further warnings about " "failure to set extended attributes."); } } else { winnt_error(status, L"Can't set extended attributes on \"%ls\"", current_path(ctx)); ret = WIMLIB_ERR_SET_XATTR; goto out; } } ret = 0; out: if (buf != _buf) FREE(buf); return ret; } /* Set the security descriptor @desc, of @desc_size bytes, on the file with open * handle @h. */ static NTSTATUS set_security_descriptor(HANDLE h, const void *_desc, size_t desc_size, struct win32_apply_ctx *ctx) { SECURITY_INFORMATION info; NTSTATUS status; SECURITY_DESCRIPTOR_RELATIVE *desc; /* * Ideally, we would just pass in the security descriptor buffer as-is. * But it turns out that Windows can mess up the security descriptor * even when using the low-level NtSetSecurityObject() function: * * - Windows will clear SE_DACL_AUTO_INHERITED if it is set in the * passed buffer. To actually get Windows to set * SE_DACL_AUTO_INHERITED, the application must set the non-persistent * flag SE_DACL_AUTO_INHERIT_REQ. As usual, Microsoft didn't bother * to properly document either of these flags. It's unclear how * important SE_DACL_AUTO_INHERITED actually is, but to be safe we use * the SE_DACL_AUTO_INHERIT_REQ workaround to set it if needed. * * - The above also applies to the equivalent SACL flags, * SE_SACL_AUTO_INHERITED and SE_SACL_AUTO_INHERIT_REQ. * * - If the application says that it's setting * DACL_SECURITY_INFORMATION, then Windows sets SE_DACL_PRESENT in the * resulting security descriptor, even if the security descriptor the * application provided did not have a DACL. This seems to be * unavoidable, since omitting DACL_SECURITY_INFORMATION would cause a * default DACL to remain. Fortunately, this behavior seems harmless, * since the resulting DACL will still be "null" --- but it will be * "the other representation of null". * * - The above also applies to SACL_SECURITY_INFORMATION and * SE_SACL_PRESENT. Again, it's seemingly unavoidable but "harmless" * that Windows changes the representation of a "null SACL". */ if (likely(desc_size <= STACK_MAX)) { desc = alloca(desc_size); } else { desc = MALLOC(desc_size); if (!desc) return STATUS_NO_MEMORY; } memcpy(desc, _desc, desc_size); if (likely(desc_size >= 4)) { if (desc->Control & SE_DACL_AUTO_INHERITED) desc->Control |= SE_DACL_AUTO_INHERIT_REQ; if (desc->Control & SE_SACL_AUTO_INHERITED) desc->Control |= SE_SACL_AUTO_INHERIT_REQ; } /* * More API insanity. We want to set the entire security descriptor * as-is. But all available APIs require specifying the specific parts * of the security descriptor being set. Especially annoying is that * mandatory integrity labels are part of the SACL, but they aren't set * with SACL_SECURITY_INFORMATION. Instead, applications must also * specify LABEL_SECURITY_INFORMATION (Windows Vista, Windows 7) or * BACKUP_SECURITY_INFORMATION (Windows 8). But at least older versions * of Windows don't error out if you provide these newer flags... * * Also, if the process isn't running as Administrator, then it probably * doesn't have SE_RESTORE_PRIVILEGE. In this case, it will always get * the STATUS_PRIVILEGE_NOT_HELD error by trying to set the SACL, even * if the security descriptor it provided did not have a SACL. By * default, in this case we try to recover and set as much of the * security descriptor as possible --- potentially excluding the DACL, and * even the owner, as well as the SACL. */ info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION | BACKUP_SECURITY_INFORMATION; /* * It's also worth noting that SetFileSecurity() is unusable because it * doesn't request "backup semantics" when it opens the file internally. * NtSetSecurityObject() seems to be the best function to use in backup * applications. (SetSecurityInfo() should also work, but it's harder * to use and must call NtSetSecurityObject() internally anyway. * BackupWrite() is theoretically usable as well, but it's inflexible * and poorly documented.) */ retry: status = NtSetSecurityObject(h, info, desc); if (NT_SUCCESS(status)) goto out_maybe_free_desc; /* Failed to set the requested parts of the security descriptor. If the * error was permissions-related, try to set fewer parts of the security * descriptor, unless WIMLIB_EXTRACT_FLAG_STRICT_ACLS is enabled. */ if ((status == STATUS_PRIVILEGE_NOT_HELD || status == STATUS_ACCESS_DENIED) && !(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS)) { if (info & SACL_SECURITY_INFORMATION) { info &= ~(SACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION | BACKUP_SECURITY_INFORMATION); ctx->partial_security_descriptors++; goto retry; } if (info & DACL_SECURITY_INFORMATION) { info &= ~DACL_SECURITY_INFORMATION; goto retry; } if (info & OWNER_SECURITY_INFORMATION) { info &= ~OWNER_SECURITY_INFORMATION; goto retry; } /* Nothing left except GROUP, and if we removed it we * wouldn't have anything at all. */ } /* No part of the security descriptor could be set, or * WIMLIB_EXTRACT_FLAG_STRICT_ACLS is enabled and the full security * descriptor could not be set. */ if (!(info & SACL_SECURITY_INFORMATION)) ctx->partial_security_descriptors--; ctx->no_security_descriptors++; out_maybe_free_desc: if (unlikely(desc_size > STACK_MAX)) FREE(desc); return status; } /* Set metadata on the open file @h from the WIM inode @inode. */ static int do_apply_metadata_to_file(HANDLE h, const struct wim_inode *inode, struct win32_apply_ctx *ctx) { FILE_BASIC_INFORMATION info; NTSTATUS status; int ret; /* Set the file's object ID if present and object IDs are supported by * the filesystem. */ set_object_id(h, inode, ctx); /* Set the file's extended attributes (EAs) if present and EAs are * supported by the filesystem. */ ret = set_xattrs(h, inode, ctx); if (ret) return ret; /* Set the file's security descriptor if present and we're not in * NO_ACLS mode */ if (inode_has_security_descriptor(inode) && !(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS)) { const struct wim_security_data *sd; const void *desc; size_t desc_size; sd = wim_get_current_security_data(ctx->common.wim); desc = sd->descriptors[inode->i_security_id]; desc_size = sd->sizes[inode->i_security_id]; status = set_security_descriptor(h, desc, desc_size, ctx); if (!NT_SUCCESS(status) && (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS)) { winnt_error(status, L"Can't set security descriptor on \"%ls\"", current_path(ctx)); return WIMLIB_ERR_SET_SECURITY; } } /* Set attributes and timestamps */ info.CreationTime.QuadPart = inode->i_creation_time; info.LastAccessTime.QuadPart = inode->i_last_access_time; info.LastWriteTime.QuadPart = inode->i_last_write_time; info.ChangeTime.QuadPart = 0; if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES) { info.FileAttributes = FILE_ATTRIBUTE_NORMAL; } else { info.FileAttributes = inode->i_attributes & ~SPECIAL_ATTRIBUTES; if (info.FileAttributes == 0) info.FileAttributes = FILE_ATTRIBUTE_NORMAL; } status = NtSetInformationFile(h, &ctx->iosb, &info, sizeof(info), FileBasicInformation); /* On FAT volumes we get STATUS_INVALID_PARAMETER if we try to set * attributes on the root directory. (Apparently because FAT doesn't * actually have a place to store those attributes!) */ if (!NT_SUCCESS(status) && !(status == STATUS_INVALID_PARAMETER && dentry_is_root(inode_first_extraction_dentry(inode)))) { winnt_error(status, L"Can't set basic metadata on \"%ls\"", current_path(ctx)); return WIMLIB_ERR_SET_ATTRIBUTES; } return 0; } static int apply_metadata_to_file(const struct wim_dentry *dentry, struct win32_apply_ctx *ctx) { const struct wim_inode *inode = dentry->d_inode; DWORD perms; HANDLE h; NTSTATUS status; int ret; perms = FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | WRITE_DAC | WRITE_OWNER | ACCESS_SYSTEM_SECURITY; build_extraction_path(dentry, ctx); /* Open a handle with as many relevant permissions as possible. */ while (!NT_SUCCESS(status = do_create_file(&h, perms, NULL, 0, FILE_OPEN, 0, ctx))) { if (status == STATUS_PRIVILEGE_NOT_HELD || status == STATUS_ACCESS_DENIED) { if (perms & ACCESS_SYSTEM_SECURITY) { perms &= ~ACCESS_SYSTEM_SECURITY; continue; } if (perms & WRITE_DAC) { perms &= ~WRITE_DAC; continue; } if (perms & WRITE_OWNER) { perms &= ~WRITE_OWNER; continue; } } winnt_error(status, L"Can't open \"%ls\" to set metadata", current_path(ctx)); return WIMLIB_ERR_OPEN; } ret = do_apply_metadata_to_file(h, inode, ctx); NtClose(h); return ret; } static int apply_metadata(struct list_head *dentry_list, struct win32_apply_ctx *ctx) { const struct wim_dentry *dentry; int ret; /* We go in reverse so that metadata is set on all a directory's * children before the directory itself. This avoids any potential * problems with attributes, timestamps, or security descriptors. */ list_for_each_entry_reverse(dentry, dentry_list, d_extraction_list_node) { ret = apply_metadata_to_file(dentry, ctx); ret = check_apply_error(dentry, ctx, ret); if (ret) return ret; ret = report_file_metadata_applied(&ctx->common); if (ret) return ret; } return 0; } /* Issue warnings about problems during the extraction for which warnings were * not already issued (due to the high number of potential warnings if we issued * them per-file). */ static void do_warnings(const struct win32_apply_ctx *ctx) { if (ctx->partial_security_descriptors == 0 && ctx->no_security_descriptors == 0 && ctx->num_set_short_name_failures == 0 #if 0 && ctx->num_remove_short_name_failures == 0 #endif ) return; WARNING("Extraction to \"%ls\" complete, but with one or more warnings:", ctx->common.target); if (ctx->num_set_short_name_failures) { WARNING("- Could not set short names on %lu files or directories", ctx->num_set_short_name_failures); } #if 0 if (ctx->num_remove_short_name_failures) { WARNING("- Could not remove short names on %lu files or directories" " (This is expected on Vista and earlier)", ctx->num_remove_short_name_failures); } #endif if (ctx->partial_security_descriptors) { WARNING("- Could only partially set the security descriptor\n" " on %lu files or directories.", ctx->partial_security_descriptors); } if (ctx->no_security_descriptors) { WARNING("- Could not set security descriptor at all\n" " on %lu files or directories.", ctx->no_security_descriptors); } if (ctx->partial_security_descriptors || ctx->no_security_descriptors) { WARNING("To fully restore all security descriptors, run the program\n" " with Administrator rights."); } } static u64 count_dentries(const struct list_head *dentry_list) { const struct list_head *cur; u64 count = 0; list_for_each(cur, dentry_list) count++; return count; } /* Extract files from a WIM image to a directory on Windows */ static int win32_extract(struct list_head *dentry_list, struct apply_ctx *_ctx) { int ret; struct win32_apply_ctx *ctx = (struct win32_apply_ctx *)_ctx; u64 dentry_count; ret = prepare_target(dentry_list, ctx); if (ret) goto out; if (unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT)) { ret = start_wimboot_extraction(dentry_list, ctx); if (ret) goto out; } ctx->windows_build_number = xml_get_windows_build_number(ctx->common.wim->xml_info, ctx->common.wim->current_image); dentry_count = count_dentries(dentry_list); ret = start_file_structure_phase(&ctx->common, dentry_count); if (ret) goto out; ret = create_directories(dentry_list, ctx); if (ret) goto out; ret = create_nondirectories(dentry_list, ctx); if (ret) goto out; ret = end_file_structure_phase(&ctx->common); if (ret) goto out; struct read_blob_callbacks cbs = { .begin_blob = win32_begin_extract_blob, .continue_blob = win32_extract_chunk, .end_blob = win32_end_extract_blob, .ctx = ctx, }; ret = extract_blob_list(&ctx->common, &cbs); if (ret) goto out; ret = start_file_metadata_phase(&ctx->common, dentry_count); if (ret) goto out; ret = apply_metadata(dentry_list, ctx); if (ret) goto out; ret = end_file_metadata_phase(&ctx->common); if (ret) goto out; if (unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT)) { ret = end_wimboot_extraction(ctx); if (ret) goto out; } do_warnings(ctx); out: close_target_directory(ctx); if (ctx->target_ntpath.Buffer) HeapFree(GetProcessHeap(), 0, ctx->target_ntpath.Buffer); FREE(ctx->pathbuf.Buffer); FREE(ctx->print_buffer); FREE(ctx->wimboot.wims); if (ctx->prepopulate_pats) { FREE(ctx->prepopulate_pats->strings); FREE(ctx->prepopulate_pats); } FREE(ctx->mem_prepopulate_pats); FREE(ctx->data_buffer); return ret; } const struct apply_operations win32_apply_ops = { .name = "Windows", .get_supported_features = win32_get_supported_features, .extract = win32_extract, .will_back_from_wim = win32_will_back_from_wim, .context_size = sizeof(struct win32_apply_ctx), }; #endif /* __WIN32__ */ wimlib-1.13.1/src/solid.c0000644000175000017500000001517113272231267012060 00000000000000/* * solid.c * * Heuristic sorting of blobs to optimize solid compression. */ /* * Copyright (C) 2015 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib/blob_table.h" #include "wimlib/dentry.h" #include "wimlib/encoding.h" #include "wimlib/endianness.h" #include "wimlib/metadata.h" #include "wimlib/paths.h" #include "wimlib/solid.h" #include "wimlib/unaligned.h" static const utf16lechar * get_extension(const utf16lechar *name, size_t nbytes) { const utf16lechar *p = name + (nbytes / sizeof(utf16lechar)); for (;;) { if (p == name) return NULL; if (*(p - 1) == cpu_to_le16('/') || *(p - 1) == cpu_to_le16('\\')) return NULL; if (*(p - 1) == cpu_to_le16('.')) return p; p--; } } /* * Sort order for solid compression: * * 1. Blobs without sort names * - sorted by sequential order * 2. Blobs with sort names: * a. Blobs whose sort name does not have an extension * - sorted by sort name * b. Blobs whose sort name has an extension * - sorted primarily by extension (case insensitive), * secondarily by sort name (case insensitive) */ static int cmp_blobs_by_solid_sort_name(const void *p1, const void *p2) { const struct blob_descriptor *blob1, *blob2; blob1 = *(const struct blob_descriptor **)p1; blob2 = *(const struct blob_descriptor **)p2; if (blob1->solid_sort_name) { if (!blob2->solid_sort_name) return 1; const utf16lechar *extension1 = get_extension(blob1->solid_sort_name, blob1->solid_sort_name_nbytes); const utf16lechar *extension2 = get_extension(blob2->solid_sort_name, blob2->solid_sort_name_nbytes); if (extension1) { if (!extension2) return 1; int res = cmp_utf16le_strings_z(extension1, extension2, true); /* case insensitive */ if (res) return res; } else { if (extension2) return -1; } int res = cmp_utf16le_strings(blob1->solid_sort_name, blob1->solid_sort_name_nbytes / sizeof(utf16lechar), blob2->solid_sort_name, blob2->solid_sort_name_nbytes / sizeof(utf16lechar), true); /* case insensitive */ if (res) return res; } else { if (blob2->solid_sort_name) return -1; } return cmp_blobs_by_sequential_order(p1, p2); } static void blob_set_solid_sort_name_from_inode(struct blob_descriptor *blob, const struct wim_inode *inode) { const struct wim_dentry *dentry; const utf16lechar *best_name = NULL; size_t best_name_nbytes = SIZE_MAX; if (blob->solid_sort_name) /* Sort name already set? */ return; /* If this file has multiple names, choose the shortest one. */ inode_for_each_dentry(dentry, inode) { if (dentry->d_name_nbytes < best_name_nbytes) { best_name = dentry->d_name; best_name_nbytes = dentry->d_name_nbytes; } } blob->solid_sort_name = utf16le_dupz(best_name, best_name_nbytes); blob->solid_sort_name_nbytes = best_name_nbytes; } struct temp_blob_table { struct hlist_head *table; size_t capacity; }; static int dentry_fill_in_solid_sort_names(struct wim_dentry *dentry, void *_blob_table) { const struct temp_blob_table *blob_table = _blob_table; const struct wim_inode *inode = dentry->d_inode; const u8 *hash; struct hlist_head *head; struct blob_descriptor *blob; hash = inode_get_hash_of_unnamed_data_stream(inode); if (!hash) /* unhashed? */ return 0; head = &blob_table->table[load_size_t_unaligned(hash) % blob_table->capacity]; hlist_for_each_entry(blob, head, hash_list_2) { if (hashes_equal(hash, blob->hash)) { blob_set_solid_sort_name_from_inode(blob, inode); break; } } return 0; } static int image_fill_in_solid_sort_names(WIMStruct *wim) { return for_dentry_in_tree(wim_get_current_root_dentry(wim), dentry_fill_in_solid_sort_names, wim->private); } int sort_blob_list_for_solid_compression(struct list_head *blob_list) { size_t num_blobs = 0; struct temp_blob_table blob_table; WIMStruct *wims[128]; int num_wims = 0; struct blob_descriptor *blob; int ret; /* Count the number of blobs to be written. */ list_for_each_entry(blob, blob_list, write_blobs_list) num_blobs++; /* Allocate a temporary hash table for mapping blob hash => blob */ blob_table.capacity = num_blobs; blob_table.table = CALLOC(blob_table.capacity, sizeof(blob_table.table[0])); if (!blob_table.table) return WIMLIB_ERR_NOMEM; /* * For each blob to be written: * - Reset the sort name * - If it's in non-solid WIM resource, then save the WIMStruct. * - If it's in a file on disk, then set its sort name from that. */ list_for_each_entry(blob, blob_list, write_blobs_list) { blob->solid_sort_name = NULL; blob->solid_sort_name_nbytes = 0; switch (blob->blob_location) { case BLOB_IN_WIM: if (blob->size != blob->rdesc->uncompressed_size) continue; for (int i = 0; i < num_wims; i++) if (blob->rdesc->wim == wims[i]) goto found_wim; if (num_wims >= ARRAY_LEN(wims)) continue; wims[num_wims++] = blob->rdesc->wim; found_wim: hlist_add_head(&blob->hash_list_2, &blob_table.table[load_size_t_unaligned(blob->hash) % blob_table.capacity]); break; case BLOB_IN_FILE_ON_DISK: #ifdef __WIN32__ case BLOB_IN_WINDOWS_FILE: #endif blob_set_solid_sort_name_from_inode(blob, blob->file_inode); break; default: break; } } /* For each WIMStruct that was found, search for dentry references to * each blob and fill in the sort name this way. This is useful e.g. * when exporting a solid WIM file from a non-solid WIM file. */ for (int i = 0; i < num_wims; i++) { if (!wim_has_metadata(wims[i])) continue; wims[i]->private = &blob_table; ret = for_image(wims[i], WIMLIB_ALL_IMAGES, image_fill_in_solid_sort_names); if (ret) goto out; deselect_current_wim_image(wims[i]); } ret = sort_blob_list(blob_list, offsetof(struct blob_descriptor, write_blobs_list), cmp_blobs_by_solid_sort_name); out: list_for_each_entry(blob, blob_list, write_blobs_list) FREE(blob->solid_sort_name); FREE(blob_table.table); return ret; } wimlib-1.13.1/src/compress.c0000644000175000017500000001136213277475672012616 00000000000000/* * compress.c * * Generic functions for compression, wrapping around actual compression * implementations. */ /* * Copyright (C) 2013, 2014 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib.h" #include "wimlib/error.h" #include "wimlib/compressor_ops.h" #include "wimlib/util.h" struct wimlib_compressor { const struct compressor_ops *ops; void *private; enum wimlib_compression_type ctype; size_t max_block_size; }; static const struct compressor_ops * const compressor_ops[] = { [WIMLIB_COMPRESSION_TYPE_XPRESS] = &xpress_compressor_ops, [WIMLIB_COMPRESSION_TYPE_LZX] = &lzx_compressor_ops, [WIMLIB_COMPRESSION_TYPE_LZMS] = &lzms_compressor_ops, }; /* Scale: 10 = low, 50 = medium, 100 = high */ #define DEFAULT_COMPRESSION_LEVEL 50 static unsigned int default_compression_levels[ARRAY_LEN(compressor_ops)]; static bool compressor_ctype_valid(int ctype) { return (ctype >= 0 && ctype < ARRAY_LEN(compressor_ops) && compressor_ops[ctype] != NULL); } WIMLIBAPI int wimlib_set_default_compression_level(int ctype, unsigned int compression_level) { if (ctype == -1) { for (int i = 0; i < ARRAY_LEN(default_compression_levels); i++) default_compression_levels[i] = compression_level; } else { if (!compressor_ctype_valid(ctype)) return WIMLIB_ERR_INVALID_COMPRESSION_TYPE; default_compression_levels[ctype] = compression_level; } return 0; } WIMLIBAPI u64 wimlib_get_compressor_needed_memory(enum wimlib_compression_type ctype, size_t max_block_size, unsigned int compression_level) { bool destructive; const struct compressor_ops *ops; u64 size; destructive = (compression_level & WIMLIB_COMPRESSOR_FLAG_DESTRUCTIVE); compression_level &= ~WIMLIB_COMPRESSOR_FLAG_DESTRUCTIVE; if (!compressor_ctype_valid(ctype)) return 0; if (compression_level > 0xFFFFFF) return 0; if (max_block_size == 0) return 0; ops = compressor_ops[ctype]; if (compression_level == 0) compression_level = default_compression_levels[ctype]; if (compression_level == 0) compression_level = DEFAULT_COMPRESSION_LEVEL; if (ops->get_needed_memory) { size = ops->get_needed_memory(max_block_size, compression_level, destructive); /* 0 is never valid and indicates an invalid max_block_size. */ if (size == 0) return 0; } else { size = 0; } return size + sizeof(struct wimlib_compressor); } WIMLIBAPI int wimlib_create_compressor(enum wimlib_compression_type ctype, size_t max_block_size, unsigned int compression_level, struct wimlib_compressor **c_ret) { bool destructive; struct wimlib_compressor *c; destructive = (compression_level & WIMLIB_COMPRESSOR_FLAG_DESTRUCTIVE); compression_level &= ~WIMLIB_COMPRESSOR_FLAG_DESTRUCTIVE; if (!compressor_ctype_valid(ctype)) return WIMLIB_ERR_INVALID_COMPRESSION_TYPE; if (compression_level > 0xFFFFFF) return WIMLIB_ERR_INVALID_PARAM; if (c_ret == NULL) return WIMLIB_ERR_INVALID_PARAM; if (max_block_size == 0) return WIMLIB_ERR_INVALID_PARAM; c = MALLOC(sizeof(*c)); if (c == NULL) return WIMLIB_ERR_NOMEM; c->ops = compressor_ops[ctype]; c->private = NULL; c->ctype = ctype; c->max_block_size = max_block_size; if (c->ops->create_compressor) { int ret; if (compression_level == 0) compression_level = default_compression_levels[ctype]; if (compression_level == 0) compression_level = DEFAULT_COMPRESSION_LEVEL; ret = c->ops->create_compressor(max_block_size, compression_level, destructive, &c->private); if (ret) { FREE(c); return ret; } } *c_ret = c; return 0; } WIMLIBAPI size_t wimlib_compress(const void *uncompressed_data, size_t uncompressed_size, void *compressed_data, size_t compressed_size_avail, struct wimlib_compressor *c) { if (unlikely(uncompressed_size == 0 || uncompressed_size > c->max_block_size)) return 0; return c->ops->compress(uncompressed_data, uncompressed_size, compressed_data, compressed_size_avail, c->private); } WIMLIBAPI void wimlib_free_compressor(struct wimlib_compressor *c) { if (c) { if (c->ops->free_compressor) c->ops->free_compressor(c->private); FREE(c); } } wimlib-1.13.1/src/avl_tree.c0000644000175000017500000005612513160354224012546 00000000000000/* * avl_tree.c - intrusive, nonrecursive AVL tree data structure (self-balancing * binary search tree), implementation file * * The following copying information applies to this specific source code file: * * Written in 2014-2016 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib/avl_tree.h" /* Returns the left child (sign < 0) or the right child (sign > 0) of the * specified AVL tree node. * Note: for all calls of this, 'sign' is constant at compilation time, * so the compiler can remove the conditional. */ static AVL_INLINE struct avl_tree_node * avl_get_child(const struct avl_tree_node *parent, int sign) { if (sign < 0) return parent->left; else return parent->right; } static AVL_INLINE struct avl_tree_node * avl_tree_first_or_last_in_order(const struct avl_tree_node *root, int sign) { const struct avl_tree_node *first = root; if (first) while (avl_get_child(first, +sign)) first = avl_get_child(first, +sign); return (struct avl_tree_node *)first; } /* Starts an in-order traversal of the tree: returns the least-valued node, or * NULL if the tree is empty. */ struct avl_tree_node * avl_tree_first_in_order(const struct avl_tree_node *root) { return avl_tree_first_or_last_in_order(root, -1); } /* Starts a *reverse* in-order traversal of the tree: returns the * greatest-valued node, or NULL if the tree is empty. */ struct avl_tree_node * avl_tree_last_in_order(const struct avl_tree_node *root) { return avl_tree_first_or_last_in_order(root, 1); } static AVL_INLINE struct avl_tree_node * avl_tree_next_or_prev_in_order(const struct avl_tree_node *node, int sign) { const struct avl_tree_node *next; if (avl_get_child(node, +sign)) for (next = avl_get_child(node, +sign); avl_get_child(next, -sign); next = avl_get_child(next, -sign)) ; else for (next = avl_get_parent(node); next && node == avl_get_child(next, +sign); node = next, next = avl_get_parent(next)) ; return (struct avl_tree_node *)next; } /* Continues an in-order traversal of the tree: returns the next-greatest-valued * node, or NULL if there is none. */ struct avl_tree_node * avl_tree_next_in_order(const struct avl_tree_node *node) { return avl_tree_next_or_prev_in_order(node, 1); } /* Continues a *reverse* in-order traversal of the tree: returns the * previous-greatest-valued node, or NULL if there is none. */ struct avl_tree_node * avl_tree_prev_in_order(const struct avl_tree_node *node) { return avl_tree_next_or_prev_in_order(node, -1); } /* Starts a postorder traversal of the tree. */ struct avl_tree_node * avl_tree_first_in_postorder(const struct avl_tree_node *root) { const struct avl_tree_node *first = root; if (first) while (first->left || first->right) first = first->left ? first->left : first->right; return (struct avl_tree_node *)first; } /* Continues a postorder traversal of the tree. @prev will not be deferenced as * it's allowed that its memory has been freed; @prev_parent must be its saved * parent node. Returns NULL if there are no more nodes (i.e. @prev was the * root of the tree). */ struct avl_tree_node * avl_tree_next_in_postorder(const struct avl_tree_node *prev, const struct avl_tree_node *prev_parent) { const struct avl_tree_node *next = prev_parent; if (next && prev == next->left && next->right) for (next = next->right; next->left || next->right; next = next->left ? next->left : next->right) ; return (struct avl_tree_node *)next; } /* Sets the left child (sign < 0) or the right child (sign > 0) of the * specified AVL tree node. * Note: for all calls of this, 'sign' is constant at compilation time, * so the compiler can remove the conditional. */ static AVL_INLINE void avl_set_child(struct avl_tree_node *parent, int sign, struct avl_tree_node *child) { if (sign < 0) parent->left = child; else parent->right = child; } /* Sets the parent and balance factor of the specified AVL tree node. */ static AVL_INLINE void avl_set_parent_balance(struct avl_tree_node *node, struct avl_tree_node *parent, int balance_factor) { node->parent_balance = (uintptr_t)parent | (balance_factor + 1); } /* Sets the parent of the specified AVL tree node. */ static AVL_INLINE void avl_set_parent(struct avl_tree_node *node, struct avl_tree_node *parent) { node->parent_balance = (uintptr_t)parent | (node->parent_balance & 3); } /* Returns the balance factor of the specified AVL tree node --- that is, the * height of its right subtree minus the height of its left subtree. */ static AVL_INLINE int avl_get_balance_factor(const struct avl_tree_node *node) { return (int)(node->parent_balance & 3) - 1; } /* Adds @amount to the balance factor of the specified AVL tree node. * The caller must ensure this still results in a valid balance factor * (-1, 0, or 1). */ static AVL_INLINE void avl_adjust_balance_factor(struct avl_tree_node *node, int amount) { node->parent_balance += amount; } static AVL_INLINE void avl_replace_child(struct avl_tree_node **root_ptr, struct avl_tree_node *parent, struct avl_tree_node *old_child, struct avl_tree_node *new_child) { if (parent) { if (old_child == parent->left) parent->left = new_child; else parent->right = new_child; } else { *root_ptr = new_child; } } /* * Template for performing a single rotation --- * * sign > 0: Rotate clockwise (right) rooted at A: * * P? P? * | | * A B * / \ / \ * B C? => D? A * / \ / \ * D? E? E? C? * * (nodes marked with ? may not exist) * * sign < 0: Rotate counterclockwise (left) rooted at A: * * P? P? * | | * A B * / \ / \ * C? B => A D? * / \ / \ * E? D? C? E? * * This updates pointers but not balance factors! */ static AVL_INLINE void avl_rotate(struct avl_tree_node ** const root_ptr, struct avl_tree_node * const A, const int sign) { struct avl_tree_node * const B = avl_get_child(A, -sign); struct avl_tree_node * const E = avl_get_child(B, +sign); struct avl_tree_node * const P = avl_get_parent(A); avl_set_child(A, -sign, E); avl_set_parent(A, B); avl_set_child(B, +sign, A); avl_set_parent(B, P); if (E) avl_set_parent(E, A); avl_replace_child(root_ptr, P, A, B); } /* * Template for performing a double rotation --- * * sign > 0: Rotate counterclockwise (left) rooted at B, then * clockwise (right) rooted at A: * * P? P? P? * | | | * A A E * / \ / \ / \ * B C? => E C? => B A * / \ / \ / \ / \ * D? E B G? D? F?G? C? * / \ / \ * F? G? D? F? * * (nodes marked with ? may not exist) * * sign < 0: Rotate clockwise (right) rooted at B, then * counterclockwise (left) rooted at A: * * P? P? P? * | | | * A A E * / \ / \ / \ * C? B => C? E => A B * / \ / \ / \ / \ * E D? G? B C? G?F? D? * / \ / \ * G? F? F? D? * * Returns a pointer to E and updates balance factors. Except for those * two things, this function is equivalent to: * avl_rotate(root_ptr, B, -sign); * avl_rotate(root_ptr, A, +sign); * * See comment in avl_handle_subtree_growth() for explanation of balance * factor updates. */ static AVL_INLINE struct avl_tree_node * avl_do_double_rotate(struct avl_tree_node ** const root_ptr, struct avl_tree_node * const B, struct avl_tree_node * const A, const int sign) { struct avl_tree_node * const E = avl_get_child(B, +sign); struct avl_tree_node * const F = avl_get_child(E, -sign); struct avl_tree_node * const G = avl_get_child(E, +sign); struct avl_tree_node * const P = avl_get_parent(A); const int e = avl_get_balance_factor(E); avl_set_child(A, -sign, G); avl_set_parent_balance(A, E, ((sign * e >= 0) ? 0 : -e)); avl_set_child(B, +sign, F); avl_set_parent_balance(B, E, ((sign * e <= 0) ? 0 : -e)); avl_set_child(E, +sign, A); avl_set_child(E, -sign, B); avl_set_parent_balance(E, P, 0); if (G) avl_set_parent(G, A); if (F) avl_set_parent(F, B); avl_replace_child(root_ptr, P, A, E); return E; } /* * This function handles the growth of a subtree due to an insertion. * * @root_ptr * Location of the tree's root pointer. * * @node * A subtree that has increased in height by 1 due to an insertion. * * @parent * Parent of @node; must not be NULL. * * @sign * -1 if @node is the left child of @parent; * +1 if @node is the right child of @parent. * * This function will adjust @parent's balance factor, then do a (single * or double) rotation if necessary. The return value will be %true if * the full AVL tree is now adequately balanced, or %false if the subtree * rooted at @parent is now adequately balanced but has increased in * height by 1, so the caller should continue up the tree. * * Note that if %false is returned, no rotation will have been done. * Indeed, a single node insertion cannot require that more than one * (single or double) rotation be done. */ static AVL_INLINE bool avl_handle_subtree_growth(struct avl_tree_node ** const root_ptr, struct avl_tree_node * const node, struct avl_tree_node * const parent, const int sign) { int old_balance_factor, new_balance_factor; old_balance_factor = avl_get_balance_factor(parent); if (old_balance_factor == 0) { avl_adjust_balance_factor(parent, sign); /* @parent is still sufficiently balanced (-1 or +1 * balance factor), but must have increased in height. * Continue up the tree. */ return false; } new_balance_factor = old_balance_factor + sign; if (new_balance_factor == 0) { avl_adjust_balance_factor(parent, sign); /* @parent is now perfectly balanced (0 balance factor). * It cannot have increased in height, so there is * nothing more to do. */ return true; } /* @parent is too left-heavy (new_balance_factor == -2) or * too right-heavy (new_balance_factor == +2). */ /* Test whether @node is left-heavy (-1 balance factor) or * right-heavy (+1 balance factor). * Note that it cannot be perfectly balanced (0 balance factor) * because here we are under the invariant that @node has * increased in height due to the insertion. */ if (sign * avl_get_balance_factor(node) > 0) { /* @node (B below) is heavy in the same direction @parent * (A below) is heavy. * * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ * The comment, diagram, and equations below assume sign < 0. * The other case is symmetric! * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ * * Do a clockwise rotation rooted at @parent (A below): * * A B * / \ / \ * B C? => D A * / \ / \ / \ * D E? F? G?E? C? * / \ * F? G? * * Before the rotation: * balance(A) = -2 * balance(B) = -1 * Let x = height(C). Then: * height(B) = x + 2 * height(D) = x + 1 * height(E) = x * max(height(F), height(G)) = x. * * After the rotation: * height(D) = max(height(F), height(G)) + 1 * = x + 1 * height(A) = max(height(E), height(C)) + 1 * = max(x, x) + 1 = x + 1 * balance(B) = 0 * balance(A) = 0 */ avl_rotate(root_ptr, parent, -sign); /* Equivalent to setting @parent's balance factor to 0. */ avl_adjust_balance_factor(parent, -sign); /* A */ /* Equivalent to setting @node's balance factor to 0. */ avl_adjust_balance_factor(node, -sign); /* B */ } else { /* @node (B below) is heavy in the direction opposite * from the direction @parent (A below) is heavy. * * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ * The comment, diagram, and equations below assume sign < 0. * The other case is symmetric! * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ * * Do a counterblockwise rotation rooted at @node (B below), * then a clockwise rotation rooted at @parent (A below): * * A A E * / \ / \ / \ * B C? => E C? => B A * / \ / \ / \ / \ * D? E B G? D? F?G? C? * / \ / \ * F? G? D? F? * * Before the rotation: * balance(A) = -2 * balance(B) = +1 * Let x = height(C). Then: * height(B) = x + 2 * height(E) = x + 1 * height(D) = x * max(height(F), height(G)) = x * * After both rotations: * height(A) = max(height(G), height(C)) + 1 * = x + 1 * balance(A) = balance(E{orig}) >= 0 ? 0 : -balance(E{orig}) * height(B) = max(height(D), height(F)) + 1 * = x + 1 * balance(B) = balance(E{orig} <= 0) ? 0 : -balance(E{orig}) * * height(E) = x + 2 * balance(E) = 0 */ avl_do_double_rotate(root_ptr, node, parent, -sign); } /* Height after rotation is unchanged; nothing more to do. */ return true; } /* Rebalance the tree after insertion of the specified node. */ void avl_tree_rebalance_after_insert(struct avl_tree_node **root_ptr, struct avl_tree_node *inserted) { struct avl_tree_node *node, *parent; bool done; inserted->left = NULL; inserted->right = NULL; node = inserted; /* Adjust balance factor of new node's parent. * No rotation will need to be done at this level. */ parent = avl_get_parent(node); if (!parent) return; if (node == parent->left) avl_adjust_balance_factor(parent, -1); else avl_adjust_balance_factor(parent, +1); if (avl_get_balance_factor(parent) == 0) /* @parent did not change in height. Nothing more to do. */ return; /* The subtree rooted at @parent increased in height by 1. */ do { /* Adjust balance factor of next ancestor. */ node = parent; parent = avl_get_parent(node); if (!parent) return; /* The subtree rooted at @node has increased in height by 1. */ if (node == parent->left) done = avl_handle_subtree_growth(root_ptr, node, parent, -1); else done = avl_handle_subtree_growth(root_ptr, node, parent, +1); } while (!done); } /* * This function handles the shrinkage of a subtree due to a deletion. * * @root_ptr * Location of the tree's root pointer. * * @parent * A node in the tree, exactly one of whose subtrees has decreased * in height by 1 due to a deletion. (This includes the case where * one of the child pointers has become NULL, since we can consider * the "NULL" subtree to have a height of 0.) * * @sign * +1 if the left subtree of @parent has decreased in height by 1; * -1 if the right subtree of @parent has decreased in height by 1. * * @left_deleted_ret * If the return value is not NULL, this will be set to %true if the * left subtree of the returned node has decreased in height by 1, * or %false if the right subtree of the returned node has decreased * in height by 1. * * This function will adjust @parent's balance factor, then do a (single * or double) rotation if necessary. The return value will be NULL if * the full AVL tree is now adequately balanced, or a pointer to the * parent of @parent if @parent is now adequately balanced but has * decreased in height by 1. Also in the latter case, *left_deleted_ret * will be set. */ static AVL_INLINE struct avl_tree_node * avl_handle_subtree_shrink(struct avl_tree_node ** const root_ptr, struct avl_tree_node *parent, const int sign, bool * const left_deleted_ret) { struct avl_tree_node *node; int old_balance_factor, new_balance_factor; old_balance_factor = avl_get_balance_factor(parent); if (old_balance_factor == 0) { /* Prior to the deletion, the subtree rooted at * @parent was perfectly balanced. It's now * unbalanced by 1, but that's okay and its height * hasn't changed. Nothing more to do. */ avl_adjust_balance_factor(parent, sign); return NULL; } new_balance_factor = old_balance_factor + sign; if (new_balance_factor == 0) { /* The subtree rooted at @parent is now perfectly * balanced, whereas before the deletion it was * unbalanced by 1. Its height must have decreased * by 1. No rotation is needed at this location, * but continue up the tree. */ avl_adjust_balance_factor(parent, sign); node = parent; } else { /* @parent is too left-heavy (new_balance_factor == -2) or * too right-heavy (new_balance_factor == +2). */ node = avl_get_child(parent, sign); /* The rotations below are similar to those done during * insertion (see avl_handle_subtree_growth()), so full * comments are not provided. The only new case is the * one where @node has a balance factor of 0, and that is * commented. */ if (sign * avl_get_balance_factor(node) >= 0) { avl_rotate(root_ptr, parent, -sign); if (avl_get_balance_factor(node) == 0) { /* * @node (B below) is perfectly balanced. * * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ * The comment, diagram, and equations * below assume sign < 0. The other case * is symmetric! * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ * * Do a clockwise rotation rooted at * @parent (A below): * * A B * / \ / \ * B C? => D A * / \ / \ / \ * D E F? G?E C? * / \ * F? G? * * Before the rotation: * balance(A) = -2 * balance(B) = 0 * Let x = height(C). Then: * height(B) = x + 2 * height(D) = x + 1 * height(E) = x + 1 * max(height(F), height(G)) = x. * * After the rotation: * height(D) = max(height(F), height(G)) + 1 * = x + 1 * height(A) = max(height(E), height(C)) + 1 * = max(x + 1, x) + 1 = x + 2 * balance(A) = -1 * balance(B) = +1 */ /* A: -2 => -1 (sign < 0) * or +2 => +1 (sign > 0) * No change needed --- that's the same as * old_balance_factor. */ /* B: 0 => +1 (sign < 0) * or 0 => -1 (sign > 0) */ avl_adjust_balance_factor(node, -sign); /* Height is unchanged; nothing more to do. */ return NULL; } else { avl_adjust_balance_factor(parent, -sign); avl_adjust_balance_factor(node, -sign); } } else { node = avl_do_double_rotate(root_ptr, node, parent, -sign); } } parent = avl_get_parent(node); if (parent) *left_deleted_ret = (node == parent->left); return parent; } /* Swaps node X, which must have 2 children, with its in-order successor, then * unlinks node X. Returns the parent of X just before unlinking, without its * balance factor having been updated to account for the unlink. */ static AVL_INLINE struct avl_tree_node * avl_tree_swap_with_successor(struct avl_tree_node **root_ptr, struct avl_tree_node *X, bool *left_deleted_ret) { struct avl_tree_node *Y, *ret; Y = X->right; if (!Y->left) { /* * P? P? P? * | | | * X Y Y * / \ / \ / \ * A Y => A X => A B? * / \ / \ * (0) B? (0) B? * * [ X unlinked, Y returned ] */ ret = Y; *left_deleted_ret = false; } else { struct avl_tree_node *Q; do { Q = Y; Y = Y->left; } while (Y->left); /* * P? P? P? * | | | * X Y Y * / \ / \ / \ * A ... => A ... => A ... * | | | * Q Q Q * / / / * Y X B? * / \ / \ * (0) B? (0) B? * * * [ X unlinked, Q returned ] */ Q->left = Y->right; if (Q->left) avl_set_parent(Q->left, Q); Y->right = X->right; avl_set_parent(X->right, Y); ret = Q; *left_deleted_ret = true; } Y->left = X->left; avl_set_parent(X->left, Y); Y->parent_balance = X->parent_balance; avl_replace_child(root_ptr, avl_get_parent(X), X, Y); return ret; } /* * Removes an item from the specified AVL tree. * * @root_ptr * Location of the AVL tree's root pointer. Indirection is needed * because the root node may change if the tree needed to be rebalanced * because of the deletion or if @node was the root node. * * @node * Pointer to the `struct avl_tree_node' embedded in the item to * remove from the tree. * * Note: This function *only* removes the node and rebalances the tree. * It does not free any memory. */ void avl_tree_remove(struct avl_tree_node **root_ptr, struct avl_tree_node *node) { struct avl_tree_node *parent; bool left_deleted = false; if (node->left && node->right) { /* @node is fully internal, with two children. Swap it * with its in-order successor (which must exist in the * right subtree of @node and can have, at most, a right * child), then unlink @node. */ parent = avl_tree_swap_with_successor(root_ptr, node, &left_deleted); /* @parent is now the parent of what was @node's in-order * successor. It cannot be NULL, since @node itself was * an ancestor of its in-order successor. * @left_deleted has been set to %true if @node's * in-order successor was the left child of @parent, * otherwise %false. */ } else { struct avl_tree_node *child; /* @node is missing at least one child. Unlink it. Set * @parent to @node's parent, and set @left_deleted to * reflect which child of @parent @node was. Or, if * @node was the root node, simply update the root node * and return. */ child = node->left ? node->left : node->right; parent = avl_get_parent(node); if (parent) { if (node == parent->left) { parent->left = child; left_deleted = true; } else { parent->right = child; left_deleted = false; } if (child) avl_set_parent(child, parent); } else { if (child) avl_set_parent(child, parent); *root_ptr = child; return; } } /* Rebalance the tree. */ do { if (left_deleted) parent = avl_handle_subtree_shrink(root_ptr, parent, +1, &left_deleted); else parent = avl_handle_subtree_shrink(root_ptr, parent, -1, &left_deleted); } while (parent); } wimlib-1.13.1/src/lzx_compress.c0000644000175000017500000030553013433451640013475 00000000000000/* * lzx_compress.c * * A compressor for the LZX compression format, as used in WIM archives. */ /* * Copyright (C) 2012-2017 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ /* * This file contains a compressor for the LZX ("Lempel-Ziv eXtended") * compression format, as used in the WIM (Windows IMaging) file format. * * Two different LZX-compatible algorithms are implemented: "near-optimal" and * "lazy". "Near-optimal" is significantly slower than "lazy", but results in a * better compression ratio. The "near-optimal" algorithm is used at the * default compression level. * * This file may need some slight modifications to be used outside of the WIM * format. In particular, in other situations the LZX block header might be * slightly different, and sliding window support might be required. * * LZX is a compression format derived from DEFLATE, the format used by zlib and * gzip. Both LZX and DEFLATE use LZ77 matching and Huffman coding. Certain * details are quite similar, such as the method for storing Huffman codes. * However, the main differences are: * * - LZX preprocesses the data to attempt to make x86 machine code slightly more * compressible before attempting to compress it further. * * - LZX uses a "main" alphabet which combines literals and matches, with the * match symbols containing a "length header" (giving all or part of the match * length) and an "offset slot" (giving, roughly speaking, the order of * magnitude of the match offset). * * - LZX does not have static Huffman blocks (that is, the kind with preset * Huffman codes); however it does have two types of dynamic Huffman blocks * ("verbatim" and "aligned"). * * - LZX has a minimum match length of 2 rather than 3. Length 2 matches can be * useful, but generally only if the compressor is smart about choosing them. * * - In LZX, offset slots 0 through 2 actually represent entries in an LRU queue * of match offsets. This is very useful for certain types of files, such as * binary files that have repeating records. */ /******************************************************************************/ /* General parameters */ /*----------------------------------------------------------------------------*/ /* * The compressor uses the faster algorithm at levels <= MAX_FAST_LEVEL. It * uses the slower algorithm at levels > MAX_FAST_LEVEL. */ #define MAX_FAST_LEVEL 34 /* * The compressor-side limits on the codeword lengths (in bits) for each Huffman * code. To make outputting bits slightly faster, some of these limits are * lower than the limits defined by the LZX format. This does not significantly * affect the compression ratio. */ #define MAIN_CODEWORD_LIMIT 16 #define LENGTH_CODEWORD_LIMIT 12 #define ALIGNED_CODEWORD_LIMIT 7 #define PRE_CODEWORD_LIMIT 7 /******************************************************************************/ /* Block splitting parameters */ /*----------------------------------------------------------------------------*/ /* * The compressor always outputs blocks of at least this size in bytes, except * for the last block which may need to be smaller. */ #define MIN_BLOCK_SIZE 6500 /* * The compressor attempts to end a block when it reaches this size in bytes. * The final size might be slightly larger due to matches extending beyond the * end of the block. Specifically: * * - The near-optimal compressor may choose a match of up to LZX_MAX_MATCH_LEN * bytes starting at position 'SOFT_MAX_BLOCK_SIZE - 1'. * * - The lazy compressor may choose a sequence of literals starting at position * 'SOFT_MAX_BLOCK_SIZE - 1' when it sees a sequence of increasingly better * matches. The final match may be up to LZX_MAX_MATCH_LEN bytes. The * length of the literal sequence is approximately limited by the "nice match * length" parameter. */ #define SOFT_MAX_BLOCK_SIZE 100000 /* * The number of observed items (matches and literals) that represents * sufficient data for the compressor to decide whether the current block should * be ended or not. */ #define NUM_OBSERVATIONS_PER_BLOCK_CHECK 400 /******************************************************************************/ /* Parameters for slower algorithm */ /*----------------------------------------------------------------------------*/ /* * The log base 2 of the number of entries in the hash table for finding length * 2 matches. This could be as high as 16, but using a smaller hash table * speeds up compression due to reduced cache pressure. */ #define BT_MATCHFINDER_HASH2_ORDER 12 /* * The number of lz_match structures in the match cache, excluding the extra * "overflow" entries. This value should be high enough so that nearly the * time, all matches found in a given block can fit in the match cache. * However, fallback behavior (immediately terminating the block) on cache * overflow is still required. */ #define CACHE_LENGTH (SOFT_MAX_BLOCK_SIZE * 5) /* * An upper bound on the number of matches that can ever be saved in the match * cache for a single position. Since each match we save for a single position * has a distinct length, we can use the number of possible match lengths in LZX * as this bound. This bound is guaranteed to be valid in all cases, although * if 'nice_match_length < LZX_MAX_MATCH_LEN', then it will never actually be * reached. */ #define MAX_MATCHES_PER_POS LZX_NUM_LENS /* * A scaling factor that makes it possible to consider fractional bit costs. A * single bit has a cost of BIT_COST. * * Note: this is only useful as a statistical trick for when the true costs are * unknown. Ultimately, each token in LZX requires a whole number of bits to * output. */ #define BIT_COST 64 /* * Should the compressor take into account the costs of aligned offset symbols * instead of assuming that all are equally likely? */ #define CONSIDER_ALIGNED_COSTS 1 /* * Should the "minimum" cost path search algorithm consider "gap" matches, where * a normal match is followed by a literal, then by a match with the same * offset? This is one specific, somewhat common situation in which the true * minimum cost path is often different from the path found by looking only one * edge ahead. */ #define CONSIDER_GAP_MATCHES 1 /******************************************************************************/ /* Includes */ /*----------------------------------------------------------------------------*/ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib/compress_common.h" #include "wimlib/compressor_ops.h" #include "wimlib/error.h" #include "wimlib/lz_extend.h" #include "wimlib/lzx_common.h" #include "wimlib/unaligned.h" #include "wimlib/util.h" /* Note: BT_MATCHFINDER_HASH2_ORDER must be defined before including * bt_matchfinder.h. */ /* Matchfinders with 16-bit positions */ #define mf_pos_t u16 #define MF_SUFFIX _16 #include "wimlib/bt_matchfinder.h" #include "wimlib/hc_matchfinder.h" /* Matchfinders with 32-bit positions */ #undef mf_pos_t #undef MF_SUFFIX #define mf_pos_t u32 #define MF_SUFFIX _32 #include "wimlib/bt_matchfinder.h" #include "wimlib/hc_matchfinder.h" /******************************************************************************/ /* Compressor structure */ /*----------------------------------------------------------------------------*/ /* Codewords for the Huffman codes */ struct lzx_codewords { u32 main[LZX_MAINCODE_MAX_NUM_SYMBOLS]; u32 len[LZX_LENCODE_NUM_SYMBOLS]; u32 aligned[LZX_ALIGNEDCODE_NUM_SYMBOLS]; }; /* * Codeword lengths, in bits, for the Huffman codes. * * A codeword length of 0 means the corresponding codeword has zero frequency. * * The main and length codes each have one extra entry for use as a sentinel. * See lzx_write_compressed_code(). */ struct lzx_lens { u8 main[LZX_MAINCODE_MAX_NUM_SYMBOLS + 1]; u8 len[LZX_LENCODE_NUM_SYMBOLS + 1]; u8 aligned[LZX_ALIGNEDCODE_NUM_SYMBOLS]; }; /* Codewords and lengths for the Huffman codes */ struct lzx_codes { struct lzx_codewords codewords; struct lzx_lens lens; }; /* Symbol frequency counters for the Huffman-encoded alphabets */ struct lzx_freqs { u32 main[LZX_MAINCODE_MAX_NUM_SYMBOLS]; u32 len[LZX_LENCODE_NUM_SYMBOLS]; u32 aligned[LZX_ALIGNEDCODE_NUM_SYMBOLS]; }; /* Block split statistics. See the "Block splitting algorithm" section later in * this file for details. */ #define NUM_LITERAL_OBSERVATION_TYPES 8 #define NUM_MATCH_OBSERVATION_TYPES 2 #define NUM_OBSERVATION_TYPES (NUM_LITERAL_OBSERVATION_TYPES + \ NUM_MATCH_OBSERVATION_TYPES) struct lzx_block_split_stats { u32 new_observations[NUM_OBSERVATION_TYPES]; u32 observations[NUM_OBSERVATION_TYPES]; u32 num_new_observations; u32 num_observations; }; /* * Represents a run of literals followed by a match or end-of-block. This * structure is needed to temporarily store items chosen by the compressor, * since items cannot be written until all items for the block have been chosen * and the block's Huffman codes have been computed. */ struct lzx_sequence { /* * Bits 9..31: the number of literals in this run. This may be 0 and * can be at most about SOFT_MAX_BLOCK_LENGTH. The literals are not * stored explicitly in this structure; instead, they are read directly * from the uncompressed data. * * Bits 0..8: the length of the match which follows the literals, or 0 * if this literal run was the last in the block, so there is no match * which follows it. This can be at most LZX_MAX_MATCH_LEN. */ u32 litrunlen_and_matchlen; #define SEQ_MATCHLEN_BITS 9 #define SEQ_MATCHLEN_MASK (((u32)1 << SEQ_MATCHLEN_BITS) - 1) /* * If 'matchlen' doesn't indicate end-of-block, then this contains: * * Bits 10..31: either the offset plus LZX_OFFSET_ADJUSTMENT or a recent * offset code, depending on the offset slot encoded in the main symbol. * * Bits 0..9: the main symbol. */ u32 adjusted_offset_and_mainsym; #define SEQ_MAINSYM_BITS 10 #define SEQ_MAINSYM_MASK (((u32)1 << SEQ_MAINSYM_BITS) - 1) } _aligned_attribute(8); /* * This structure represents a byte position in the input buffer and a node in * the graph of possible match/literal choices. * * Logically, each incoming edge to this node is labeled with a literal or a * match that can be taken to reach this position from an earlier position; and * each outgoing edge from this node is labeled with a literal or a match that * can be taken to advance from this position to a later position. */ struct lzx_optimum_node { /* The cost, in bits, of the lowest-cost path that has been found to * reach this position. This can change as progressively lower cost * paths are found to reach this position. */ u32 cost; /* * The best arrival to this node, i.e. the match or literal that was * used to arrive to this position at the given 'cost'. This can change * as progressively lower cost paths are found to reach this position. * * For non-gap matches, this variable is divided into two bitfields * whose meanings depend on the item type: * * Literals: * Low bits are 0, high bits are the literal. * * Explicit offset matches: * Low bits are the match length, high bits are the offset plus * LZX_OFFSET_ADJUSTMENT. * * Repeat offset matches: * Low bits are the match length, high bits are the queue index. * * For gap matches, identified by OPTIMUM_GAP_MATCH set, special * behavior applies --- see the code. */ u32 item; #define OPTIMUM_OFFSET_SHIFT SEQ_MATCHLEN_BITS #define OPTIMUM_LEN_MASK SEQ_MATCHLEN_MASK #if CONSIDER_GAP_MATCHES # define OPTIMUM_GAP_MATCH 0x80000000 #endif } _aligned_attribute(8); /* The cost model for near-optimal parsing */ struct lzx_costs { /* * 'match_cost[offset_slot][len - LZX_MIN_MATCH_LEN]' is the cost of a * length 'len' match which has an offset belonging to 'offset_slot'. * The cost includes the main symbol, the length symbol if required, and * the extra offset bits if any, excluding any entropy-coded bits * (aligned offset bits). It does *not* include the cost of the aligned * offset symbol which may be required. */ u16 match_cost[LZX_MAX_OFFSET_SLOTS][LZX_NUM_LENS]; /* Cost of each symbol in the main code */ u32 main[LZX_MAINCODE_MAX_NUM_SYMBOLS]; /* Cost of each symbol in the length code */ u32 len[LZX_LENCODE_NUM_SYMBOLS]; #if CONSIDER_ALIGNED_COSTS /* Cost of each symbol in the aligned offset code */ u32 aligned[LZX_ALIGNEDCODE_NUM_SYMBOLS]; #endif }; struct lzx_output_bitstream; /* The main LZX compressor structure */ struct lzx_compressor { /* The buffer for preprocessed input data, if not using destructive * compression */ void *in_buffer; /* If true, then the compressor need not preserve the input buffer if it * compresses the data successfully */ bool destructive; /* Pointer to the compress() implementation chosen at allocation time */ void (*impl)(struct lzx_compressor *, const u8 *, size_t, struct lzx_output_bitstream *); /* The log base 2 of the window size for match offset encoding purposes. * This will be >= LZX_MIN_WINDOW_ORDER and <= LZX_MAX_WINDOW_ORDER. */ unsigned window_order; /* The number of symbols in the main alphabet. This depends on the * window order, since the window order determines the maximum possible * match offset. */ unsigned num_main_syms; /* The "nice" match length: if a match of this length is found, then it * is chosen immediately without further consideration. */ unsigned nice_match_length; /* The maximum search depth: at most this many potential matches are * considered at each position. */ unsigned max_search_depth; /* The number of optimization passes per block */ unsigned num_optim_passes; /* The symbol frequency counters for the current block */ struct lzx_freqs freqs; /* Block split statistics for the current block */ struct lzx_block_split_stats split_stats; /* The Huffman codes for the current and previous blocks. The one with * index 'codes_index' is for the current block, and the other one is * for the previous block. */ struct lzx_codes codes[2]; unsigned codes_index; /* The matches and literals that the compressor has chosen for the * current block. The required length of this array is limited by the * maximum number of matches that can ever be chosen for a single block, * plus one for the special entry at the end. */ struct lzx_sequence chosen_sequences[ DIV_ROUND_UP(SOFT_MAX_BLOCK_SIZE, LZX_MIN_MATCH_LEN) + 1]; /* Tables for mapping adjusted offsets to offset slots */ u8 offset_slot_tab_1[32768]; /* offset slots [0, 29] */ u8 offset_slot_tab_2[128]; /* offset slots [30, 49] */ union { /* Data for lzx_compress_lazy() */ struct { /* Hash chains matchfinder (MUST BE LAST!!!) */ union { struct hc_matchfinder_16 hc_mf_16; struct hc_matchfinder_32 hc_mf_32; }; }; /* Data for lzx_compress_near_optimal() */ struct { /* * Array of nodes, one per position, for running the * minimum-cost path algorithm. * * This array must be large enough to accommodate the * worst-case number of nodes, which occurs if the * compressor finds a match of length LZX_MAX_MATCH_LEN * at position 'SOFT_MAX_BLOCK_SIZE - 1', producing a * block of size 'SOFT_MAX_BLOCK_SIZE - 1 + * LZX_MAX_MATCH_LEN'. Add one for the end-of-block * node. */ struct lzx_optimum_node optimum_nodes[ SOFT_MAX_BLOCK_SIZE - 1 + LZX_MAX_MATCH_LEN + 1]; /* The cost model for the current optimization pass */ struct lzx_costs costs; /* * Cached matches for the current block. This array * contains the matches that were found at each position * in the block. Specifically, for each position, there * is a special 'struct lz_match' whose 'length' field * contains the number of matches that were found at * that position; this is followed by the matches * themselves, if any, sorted by strictly increasing * length. * * Note: in rare cases, there will be a very high number * of matches in the block and this array will overflow. * If this happens, we force the end of the current * block. CACHE_LENGTH is the length at which we * actually check for overflow. The extra slots beyond * this are enough to absorb the worst case overflow, * which occurs if starting at &match_cache[CACHE_LENGTH * - 1], we write the match count header, then write * MAX_MATCHES_PER_POS matches, then skip searching for * matches at 'LZX_MAX_MATCH_LEN - 1' positions and * write the match count header for each. */ struct lz_match match_cache[CACHE_LENGTH + MAX_MATCHES_PER_POS + LZX_MAX_MATCH_LEN - 1]; /* Binary trees matchfinder (MUST BE LAST!!!) */ union { struct bt_matchfinder_16 bt_mf_16; struct bt_matchfinder_32 bt_mf_32; }; }; }; }; /******************************************************************************/ /* Matchfinder utilities */ /*----------------------------------------------------------------------------*/ /* * Will a matchfinder using 16-bit positions be sufficient for compressing * buffers of up to the specified size? The limit could be 65536 bytes, but we * also want to optimize out the use of offset_slot_tab_2 in the 16-bit case. * This requires that the limit be no more than the length of offset_slot_tab_1 * (currently 32768). */ static forceinline bool lzx_is_16_bit(size_t max_bufsize) { STATIC_ASSERT(ARRAY_LEN(((struct lzx_compressor *)0)->offset_slot_tab_1) == 32768); return max_bufsize <= 32768; } /* * Return the offset slot for the specified adjusted match offset. */ static forceinline unsigned lzx_get_offset_slot(struct lzx_compressor *c, u32 adjusted_offset, bool is_16_bit) { if (__builtin_constant_p(adjusted_offset) && adjusted_offset < LZX_NUM_RECENT_OFFSETS) return adjusted_offset; if (is_16_bit || adjusted_offset < ARRAY_LEN(c->offset_slot_tab_1)) return c->offset_slot_tab_1[adjusted_offset]; return c->offset_slot_tab_2[adjusted_offset >> 14]; } /* * For a match that has the specified length and adjusted offset, tally its main * symbol, and if needed its length symbol; then return its main symbol. */ static forceinline unsigned lzx_tally_main_and_lensyms(struct lzx_compressor *c, unsigned length, u32 adjusted_offset, bool is_16_bit) { unsigned mainsym; if (length >= LZX_MIN_SECONDARY_LEN) { /* Length symbol needed */ c->freqs.len[length - LZX_MIN_SECONDARY_LEN]++; mainsym = LZX_NUM_CHARS + LZX_NUM_PRIMARY_LENS; } else { /* No length symbol needed */ mainsym = LZX_NUM_CHARS + length - LZX_MIN_MATCH_LEN; } mainsym += LZX_NUM_LEN_HEADERS * lzx_get_offset_slot(c, adjusted_offset, is_16_bit); c->freqs.main[mainsym]++; return mainsym; } /* * The following macros call either the 16-bit or the 32-bit version of a * matchfinder function based on the value of 'is_16_bit', which will be known * at compilation time. */ #define CALL_HC_MF(is_16_bit, c, funcname, ...) \ ((is_16_bit) ? CONCAT(funcname, _16)(&(c)->hc_mf_16, ##__VA_ARGS__) : \ CONCAT(funcname, _32)(&(c)->hc_mf_32, ##__VA_ARGS__)); #define CALL_BT_MF(is_16_bit, c, funcname, ...) \ ((is_16_bit) ? CONCAT(funcname, _16)(&(c)->bt_mf_16, ##__VA_ARGS__) : \ CONCAT(funcname, _32)(&(c)->bt_mf_32, ##__VA_ARGS__)); /******************************************************************************/ /* Output bitstream */ /*----------------------------------------------------------------------------*/ /* * The LZX bitstream is encoded as a sequence of little endian 16-bit coding * units. Bits are ordered from most significant to least significant within * each coding unit. */ /* * Structure to keep track of the current state of sending bits to the * compressed output buffer. */ struct lzx_output_bitstream { /* Bits that haven't yet been written to the output buffer */ machine_word_t bitbuf; /* Number of bits currently held in @bitbuf */ machine_word_t bitcount; /* Pointer to the start of the output buffer */ u8 *start; /* Pointer to the position in the output buffer at which the next coding * unit should be written */ u8 *next; /* Pointer to just past the end of the output buffer, rounded down by * one byte if needed to make 'end - start' a multiple of 2 */ u8 *end; }; /* Can the specified number of bits always be added to 'bitbuf' after all * pending 16-bit coding units have been flushed? */ #define CAN_BUFFER(n) ((n) <= WORDBITS - 15) /* Initialize the output bitstream to write to the specified buffer. */ static void lzx_init_output(struct lzx_output_bitstream *os, void *buffer, size_t size) { os->bitbuf = 0; os->bitcount = 0; os->start = buffer; os->next = buffer; os->end = (u8 *)buffer + (size & ~1); } /* * Add some bits to the bitbuffer variable of the output bitstream. The caller * must make sure there is enough room. */ static forceinline void lzx_add_bits(struct lzx_output_bitstream *os, u32 bits, unsigned num_bits) { os->bitbuf = (os->bitbuf << num_bits) | bits; os->bitcount += num_bits; } /* * Flush bits from the bitbuffer variable to the output buffer. 'max_num_bits' * specifies the maximum number of bits that may have been added since the last * flush. */ static forceinline void lzx_flush_bits(struct lzx_output_bitstream *os, unsigned max_num_bits) { /* Masking the number of bits to shift is only needed to avoid undefined * behavior; we don't actually care about the results of bad shifts. On * x86, the explicit masking generates no extra code. */ const u32 shift_mask = WORDBITS - 1; if (os->end - os->next < 6) return; put_unaligned_le16(os->bitbuf >> ((os->bitcount - 16) & shift_mask), os->next + 0); if (max_num_bits > 16) put_unaligned_le16(os->bitbuf >> ((os->bitcount - 32) & shift_mask), os->next + 2); if (max_num_bits > 32) put_unaligned_le16(os->bitbuf >> ((os->bitcount - 48) & shift_mask), os->next + 4); os->next += (os->bitcount >> 4) << 1; os->bitcount &= 15; } /* Add at most 16 bits to the bitbuffer and flush it. */ static forceinline void lzx_write_bits(struct lzx_output_bitstream *os, u32 bits, unsigned num_bits) { lzx_add_bits(os, bits, num_bits); lzx_flush_bits(os, 16); } /* * Flush the last coding unit to the output buffer if needed. Return the total * number of bytes written to the output buffer, or 0 if an overflow occurred. */ static size_t lzx_flush_output(struct lzx_output_bitstream *os) { if (os->end - os->next < 6) return 0; if (os->bitcount != 0) { put_unaligned_le16(os->bitbuf << (16 - os->bitcount), os->next); os->next += 2; } return os->next - os->start; } /******************************************************************************/ /* Preparing Huffman codes */ /*----------------------------------------------------------------------------*/ /* * Build the Huffman codes. This takes as input the frequency tables for each * code and produces as output a set of tables that map symbols to codewords and * codeword lengths. */ static void lzx_build_huffman_codes(struct lzx_compressor *c) { const struct lzx_freqs *freqs = &c->freqs; struct lzx_codes *codes = &c->codes[c->codes_index]; STATIC_ASSERT(MAIN_CODEWORD_LIMIT >= 9 && MAIN_CODEWORD_LIMIT <= LZX_MAX_MAIN_CODEWORD_LEN); make_canonical_huffman_code(c->num_main_syms, MAIN_CODEWORD_LIMIT, freqs->main, codes->lens.main, codes->codewords.main); STATIC_ASSERT(LENGTH_CODEWORD_LIMIT >= 8 && LENGTH_CODEWORD_LIMIT <= LZX_MAX_LEN_CODEWORD_LEN); make_canonical_huffman_code(LZX_LENCODE_NUM_SYMBOLS, LENGTH_CODEWORD_LIMIT, freqs->len, codes->lens.len, codes->codewords.len); STATIC_ASSERT(ALIGNED_CODEWORD_LIMIT >= LZX_NUM_ALIGNED_OFFSET_BITS && ALIGNED_CODEWORD_LIMIT <= LZX_MAX_ALIGNED_CODEWORD_LEN); make_canonical_huffman_code(LZX_ALIGNEDCODE_NUM_SYMBOLS, ALIGNED_CODEWORD_LIMIT, freqs->aligned, codes->lens.aligned, codes->codewords.aligned); } /* Reset the symbol frequencies for the current block. */ static void lzx_reset_symbol_frequencies(struct lzx_compressor *c) { memset(&c->freqs, 0, sizeof(c->freqs)); } static unsigned lzx_compute_precode_items(const u8 lens[restrict], const u8 prev_lens[restrict], u32 precode_freqs[restrict], unsigned precode_items[restrict]) { unsigned *itemptr; unsigned run_start; unsigned run_end; unsigned extra_bits; int delta; u8 len; itemptr = precode_items; run_start = 0; while (!((len = lens[run_start]) & 0x80)) { /* len = the length being repeated */ /* Find the next run of codeword lengths. */ run_end = run_start + 1; /* Fast case for a single length. */ if (likely(len != lens[run_end])) { delta = prev_lens[run_start] - len; if (delta < 0) delta += 17; precode_freqs[delta]++; *itemptr++ = delta; run_start++; continue; } /* Extend the run. */ do { run_end++; } while (len == lens[run_end]); if (len == 0) { /* Run of zeroes. */ /* Symbol 18: RLE 20 to 51 zeroes at a time. */ while ((run_end - run_start) >= 20) { extra_bits = min((run_end - run_start) - 20, 0x1F); precode_freqs[18]++; *itemptr++ = 18 | (extra_bits << 5); run_start += 20 + extra_bits; } /* Symbol 17: RLE 4 to 19 zeroes at a time. */ if ((run_end - run_start) >= 4) { extra_bits = min((run_end - run_start) - 4, 0xF); precode_freqs[17]++; *itemptr++ = 17 | (extra_bits << 5); run_start += 4 + extra_bits; } } else { /* A run of nonzero lengths. */ /* Symbol 19: RLE 4 to 5 of any length at a time. */ while ((run_end - run_start) >= 4) { extra_bits = (run_end - run_start) > 4; delta = prev_lens[run_start] - len; if (delta < 0) delta += 17; precode_freqs[19]++; precode_freqs[delta]++; *itemptr++ = 19 | (extra_bits << 5) | (delta << 6); run_start += 4 + extra_bits; } } /* Output any remaining lengths without RLE. */ while (run_start != run_end) { delta = prev_lens[run_start] - len; if (delta < 0) delta += 17; precode_freqs[delta]++; *itemptr++ = delta; run_start++; } } return itemptr - precode_items; } /******************************************************************************/ /* Outputting compressed data */ /*----------------------------------------------------------------------------*/ /* * Output a Huffman code in the compressed form used in LZX. * * The Huffman code is represented in the output as a logical series of codeword * lengths from which the Huffman code, which must be in canonical form, can be * reconstructed. * * The codeword lengths are themselves compressed using a separate Huffman code, * the "precode", which contains a symbol for each possible codeword length in * the larger code as well as several special symbols to represent repeated * codeword lengths (a form of run-length encoding). The precode is itself * constructed in canonical form, and its codeword lengths are represented * literally in 20 4-bit fields that immediately precede the compressed codeword * lengths of the larger code. * * Furthermore, the codeword lengths of the larger code are actually represented * as deltas from the codeword lengths of the corresponding code in the previous * block. * * @os: * Bitstream to which to write the compressed Huffman code. * @lens: * The codeword lengths, indexed by symbol, in the Huffman code. * @prev_lens: * The codeword lengths, indexed by symbol, in the corresponding Huffman * code in the previous block, or all zeroes if this is the first block. * @num_lens: * The number of symbols in the Huffman code. */ static void lzx_write_compressed_code(struct lzx_output_bitstream *os, const u8 lens[restrict], const u8 prev_lens[restrict], unsigned num_lens) { u32 precode_freqs[LZX_PRECODE_NUM_SYMBOLS]; u8 precode_lens[LZX_PRECODE_NUM_SYMBOLS]; u32 precode_codewords[LZX_PRECODE_NUM_SYMBOLS]; unsigned precode_items[num_lens]; unsigned num_precode_items; unsigned precode_item; unsigned precode_sym; unsigned i; u8 saved = lens[num_lens]; *(u8 *)(lens + num_lens) = 0x80; for (i = 0; i < LZX_PRECODE_NUM_SYMBOLS; i++) precode_freqs[i] = 0; /* Compute the "items" (RLE / literal tokens and extra bits) with which * the codeword lengths in the larger code will be output. */ num_precode_items = lzx_compute_precode_items(lens, prev_lens, precode_freqs, precode_items); /* Build the precode. */ STATIC_ASSERT(PRE_CODEWORD_LIMIT >= 5 && PRE_CODEWORD_LIMIT <= LZX_MAX_PRE_CODEWORD_LEN); make_canonical_huffman_code(LZX_PRECODE_NUM_SYMBOLS, PRE_CODEWORD_LIMIT, precode_freqs, precode_lens, precode_codewords); /* Output the lengths of the codewords in the precode. */ for (i = 0; i < LZX_PRECODE_NUM_SYMBOLS; i++) lzx_write_bits(os, precode_lens[i], LZX_PRECODE_ELEMENT_SIZE); /* Output the encoded lengths of the codewords in the larger code. */ for (i = 0; i < num_precode_items; i++) { precode_item = precode_items[i]; precode_sym = precode_item & 0x1F; lzx_add_bits(os, precode_codewords[precode_sym], precode_lens[precode_sym]); if (precode_sym >= 17) { if (precode_sym == 17) { lzx_add_bits(os, precode_item >> 5, 4); } else if (precode_sym == 18) { lzx_add_bits(os, precode_item >> 5, 5); } else { lzx_add_bits(os, (precode_item >> 5) & 1, 1); precode_sym = precode_item >> 6; lzx_add_bits(os, precode_codewords[precode_sym], precode_lens[precode_sym]); } } STATIC_ASSERT(CAN_BUFFER(2 * PRE_CODEWORD_LIMIT + 1)); lzx_flush_bits(os, 2 * PRE_CODEWORD_LIMIT + 1); } *(u8 *)(lens + num_lens) = saved; } /* * Write all matches and literal bytes (which were precomputed) in an LZX * compressed block to the output bitstream in the final compressed * representation. * * @os * The output bitstream. * @block_type * The chosen type of the LZX compressed block (LZX_BLOCKTYPE_ALIGNED or * LZX_BLOCKTYPE_VERBATIM). * @block_data * The uncompressed data of the block. * @sequences * The matches and literals to output, given as a series of sequences. * @codes * The main, length, and aligned offset Huffman codes for the block. */ static void lzx_write_sequences(struct lzx_output_bitstream *os, int block_type, const u8 *block_data, const struct lzx_sequence sequences[], const struct lzx_codes *codes) { const struct lzx_sequence *seq = sequences; unsigned min_aligned_offset_slot; if (block_type == LZX_BLOCKTYPE_ALIGNED) min_aligned_offset_slot = LZX_MIN_ALIGNED_OFFSET_SLOT; else min_aligned_offset_slot = LZX_MAX_OFFSET_SLOTS; for (;;) { /* Output the next sequence. */ u32 litrunlen = seq->litrunlen_and_matchlen >> SEQ_MATCHLEN_BITS; unsigned matchlen = seq->litrunlen_and_matchlen & SEQ_MATCHLEN_MASK; STATIC_ASSERT((u32)~SEQ_MATCHLEN_MASK >> SEQ_MATCHLEN_BITS >= SOFT_MAX_BLOCK_SIZE); u32 adjusted_offset; unsigned main_symbol; unsigned offset_slot; unsigned num_extra_bits; u32 extra_bits; /* Output the literal run of the sequence. */ if (litrunlen) { /* Is the literal run nonempty? */ /* Verify optimization is enabled on 64-bit */ STATIC_ASSERT(WORDBITS < 64 || CAN_BUFFER(3 * MAIN_CODEWORD_LIMIT)); if (CAN_BUFFER(3 * MAIN_CODEWORD_LIMIT)) { /* 64-bit: write 3 literals at a time. */ while (litrunlen >= 3) { unsigned lit0 = block_data[0]; unsigned lit1 = block_data[1]; unsigned lit2 = block_data[2]; lzx_add_bits(os, codes->codewords.main[lit0], codes->lens.main[lit0]); lzx_add_bits(os, codes->codewords.main[lit1], codes->lens.main[lit1]); lzx_add_bits(os, codes->codewords.main[lit2], codes->lens.main[lit2]); lzx_flush_bits(os, 3 * MAIN_CODEWORD_LIMIT); block_data += 3; litrunlen -= 3; } if (litrunlen--) { unsigned lit = *block_data++; lzx_add_bits(os, codes->codewords.main[lit], codes->lens.main[lit]); if (litrunlen--) { unsigned lit = *block_data++; lzx_add_bits(os, codes->codewords.main[lit], codes->lens.main[lit]); lzx_flush_bits(os, 2 * MAIN_CODEWORD_LIMIT); } else { lzx_flush_bits(os, 1 * MAIN_CODEWORD_LIMIT); } } } else { /* 32-bit: write 1 literal at a time. */ do { unsigned lit = *block_data++; lzx_add_bits(os, codes->codewords.main[lit], codes->lens.main[lit]); lzx_flush_bits(os, MAIN_CODEWORD_LIMIT); } while (--litrunlen); } } /* Was this the last literal run? */ if (matchlen == 0) return; /* Nope; output the match. */ block_data += matchlen; adjusted_offset = seq->adjusted_offset_and_mainsym >> SEQ_MAINSYM_BITS; main_symbol = seq->adjusted_offset_and_mainsym & SEQ_MAINSYM_MASK; offset_slot = (main_symbol - LZX_NUM_CHARS) / LZX_NUM_LEN_HEADERS; num_extra_bits = lzx_extra_offset_bits[offset_slot]; extra_bits = adjusted_offset - (lzx_offset_slot_base[offset_slot] + LZX_OFFSET_ADJUSTMENT); #define MAX_MATCH_BITS (MAIN_CODEWORD_LIMIT + \ LENGTH_CODEWORD_LIMIT + \ LZX_MAX_NUM_EXTRA_BITS - \ LZX_NUM_ALIGNED_OFFSET_BITS + \ ALIGNED_CODEWORD_LIMIT) /* Verify optimization is enabled on 64-bit */ STATIC_ASSERT(WORDBITS < 64 || CAN_BUFFER(MAX_MATCH_BITS)); /* Output the main symbol for the match. */ lzx_add_bits(os, codes->codewords.main[main_symbol], codes->lens.main[main_symbol]); if (!CAN_BUFFER(MAX_MATCH_BITS)) lzx_flush_bits(os, MAIN_CODEWORD_LIMIT); /* If needed, output the length symbol for the match. */ if (matchlen >= LZX_MIN_SECONDARY_LEN) { lzx_add_bits(os, codes->codewords.len[matchlen - LZX_MIN_SECONDARY_LEN], codes->lens.len[matchlen - LZX_MIN_SECONDARY_LEN]); if (!CAN_BUFFER(MAX_MATCH_BITS)) lzx_flush_bits(os, LENGTH_CODEWORD_LIMIT); } /* Output the extra offset bits for the match. In aligned * offset blocks, the lowest 3 bits of the adjusted offset are * Huffman-encoded using the aligned offset code, provided that * there are at least extra 3 offset bits required. All other * extra offset bits are output verbatim. */ if (offset_slot >= min_aligned_offset_slot) { lzx_add_bits(os, extra_bits >> LZX_NUM_ALIGNED_OFFSET_BITS, num_extra_bits - LZX_NUM_ALIGNED_OFFSET_BITS); if (!CAN_BUFFER(MAX_MATCH_BITS)) lzx_flush_bits(os, LZX_MAX_NUM_EXTRA_BITS - LZX_NUM_ALIGNED_OFFSET_BITS); lzx_add_bits(os, codes->codewords.aligned[adjusted_offset & LZX_ALIGNED_OFFSET_BITMASK], codes->lens.aligned[adjusted_offset & LZX_ALIGNED_OFFSET_BITMASK]); if (!CAN_BUFFER(MAX_MATCH_BITS)) lzx_flush_bits(os, ALIGNED_CODEWORD_LIMIT); } else { STATIC_ASSERT(CAN_BUFFER(LZX_MAX_NUM_EXTRA_BITS)); lzx_add_bits(os, extra_bits, num_extra_bits); if (!CAN_BUFFER(MAX_MATCH_BITS)) lzx_flush_bits(os, LZX_MAX_NUM_EXTRA_BITS); } if (CAN_BUFFER(MAX_MATCH_BITS)) lzx_flush_bits(os, MAX_MATCH_BITS); /* Advance to the next sequence. */ seq++; } } static void lzx_write_compressed_block(const u8 *block_begin, int block_type, u32 block_size, unsigned window_order, unsigned num_main_syms, const struct lzx_sequence sequences[], const struct lzx_codes * codes, const struct lzx_lens * prev_lens, struct lzx_output_bitstream * os) { /* The first three bits indicate the type of block and are one of the * LZX_BLOCKTYPE_* constants. */ lzx_write_bits(os, block_type, 3); /* * Output the block size. * * The original LZX format encoded the block size in 24 bits. However, * the LZX format used in WIM archives uses 1 bit to specify whether the * block has the default size of 32768 bytes, then optionally 16 bits to * specify a non-default size. This works fine for Microsoft's WIM * software (WIMGAPI), which never compresses more than 32768 bytes at a * time with LZX. However, as an extension, our LZX compressor supports * compressing up to 2097152 bytes, with a corresponding increase in * window size. It is possible for blocks in these larger buffers to * exceed 65535 bytes; such blocks cannot have their size represented in * 16 bits. * * The chosen solution was to use 24 bits for the block size when * possibly required --- specifically, when the compressor has been * allocated to be capable of compressing more than 32768 bytes at once * (which also causes the number of main symbols to be increased). */ if (block_size == LZX_DEFAULT_BLOCK_SIZE) { lzx_write_bits(os, 1, 1); } else { lzx_write_bits(os, 0, 1); if (window_order >= 16) lzx_write_bits(os, block_size >> 16, 8); lzx_write_bits(os, block_size & 0xFFFF, 16); } /* If it's an aligned offset block, output the aligned offset code. */ if (block_type == LZX_BLOCKTYPE_ALIGNED) { for (int i = 0; i < LZX_ALIGNEDCODE_NUM_SYMBOLS; i++) { lzx_write_bits(os, codes->lens.aligned[i], LZX_ALIGNEDCODE_ELEMENT_SIZE); } } /* Output the main code (two parts). */ lzx_write_compressed_code(os, codes->lens.main, prev_lens->main, LZX_NUM_CHARS); lzx_write_compressed_code(os, codes->lens.main + LZX_NUM_CHARS, prev_lens->main + LZX_NUM_CHARS, num_main_syms - LZX_NUM_CHARS); /* Output the length code. */ lzx_write_compressed_code(os, codes->lens.len, prev_lens->len, LZX_LENCODE_NUM_SYMBOLS); /* Output the compressed matches and literals. */ lzx_write_sequences(os, block_type, block_begin, sequences, codes); } /* * Given the frequencies of symbols in an LZX-compressed block and the * corresponding Huffman codes, return LZX_BLOCKTYPE_ALIGNED or * LZX_BLOCKTYPE_VERBATIM if an aligned offset or verbatim block, respectively, * will take fewer bits to output. */ static int lzx_choose_verbatim_or_aligned(const struct lzx_freqs * freqs, const struct lzx_codes * codes) { u32 verbatim_cost = 0; u32 aligned_cost = 0; /* A verbatim block requires 3 bits in each place that an aligned offset * symbol would be used in an aligned offset block. */ for (unsigned i = 0; i < LZX_ALIGNEDCODE_NUM_SYMBOLS; i++) { verbatim_cost += LZX_NUM_ALIGNED_OFFSET_BITS * freqs->aligned[i]; aligned_cost += codes->lens.aligned[i] * freqs->aligned[i]; } /* Account for the cost of sending the codeword lengths of the aligned * offset code. */ aligned_cost += LZX_ALIGNEDCODE_ELEMENT_SIZE * LZX_ALIGNEDCODE_NUM_SYMBOLS; if (aligned_cost < verbatim_cost) return LZX_BLOCKTYPE_ALIGNED; else return LZX_BLOCKTYPE_VERBATIM; } /* * Flush an LZX block: * * 1. Build the Huffman codes. * 2. Decide whether to output the block as VERBATIM or ALIGNED. * 3. Write the block. * 4. Swap the indices of the current and previous Huffman codes. * * Note: we never output UNCOMPRESSED blocks. This probably should be * implemented sometime, but it doesn't make much difference. */ static void lzx_flush_block(struct lzx_compressor *c, struct lzx_output_bitstream *os, const u8 *block_begin, u32 block_size, u32 seq_idx) { int block_type; lzx_build_huffman_codes(c); block_type = lzx_choose_verbatim_or_aligned(&c->freqs, &c->codes[c->codes_index]); lzx_write_compressed_block(block_begin, block_type, block_size, c->window_order, c->num_main_syms, &c->chosen_sequences[seq_idx], &c->codes[c->codes_index], &c->codes[c->codes_index ^ 1].lens, os); c->codes_index ^= 1; } /******************************************************************************/ /* Block splitting algorithm */ /*----------------------------------------------------------------------------*/ /* * The problem of block splitting is to decide when it is worthwhile to start a * new block with new entropy codes. There is a theoretically optimal solution: * recursively consider every possible block split, considering the exact cost * of each block, and choose the minimum cost approach. But this is far too * slow. Instead, as an approximation, we can count symbols and after every N * symbols, compare the expected distribution of symbols based on the previous * data with the actual distribution. If they differ "by enough", then start a * new block. * * As an optimization and heuristic, we don't distinguish between every symbol * but rather we combine many symbols into a single "observation type". For * literals we only look at the high bits and low bits, and for matches we only * look at whether the match is long or not. The assumption is that for typical * "real" data, places that are good block boundaries will tend to be noticable * based only on changes in these aggregate frequencies, without looking for * subtle differences in individual symbols. For example, a change from ASCII * bytes to non-ASCII bytes, or from few matches (generally less compressible) * to many matches (generally more compressible), would be easily noticed based * on the aggregates. * * For determining whether the frequency distributions are "different enough" to * start a new block, the simply heuristic of splitting when the sum of absolute * differences exceeds a constant seems to be good enough. * * Finally, for an approximation, it is not strictly necessary that the exact * symbols being used are considered. With "near-optimal parsing", for example, * the actual symbols that will be used are unknown until after the block * boundary is chosen and the block has been optimized. Since the final choices * cannot be used, we can use preliminary "greedy" choices instead. */ /* Initialize the block split statistics when starting a new block. */ static void lzx_init_block_split_stats(struct lzx_block_split_stats *stats) { memset(stats, 0, sizeof(*stats)); } /* Literal observation. Heuristic: use the top 2 bits and low 1 bits of the * literal, for 8 possible literal observation types. */ static forceinline void lzx_observe_literal(struct lzx_block_split_stats *stats, u8 lit) { stats->new_observations[((lit >> 5) & 0x6) | (lit & 1)]++; stats->num_new_observations++; } /* Match observation. Heuristic: use one observation type for "short match" and * one observation type for "long match". */ static forceinline void lzx_observe_match(struct lzx_block_split_stats *stats, unsigned length) { stats->new_observations[NUM_LITERAL_OBSERVATION_TYPES + (length >= 5)]++; stats->num_new_observations++; } static bool lzx_should_end_block(struct lzx_block_split_stats *stats) { if (stats->num_observations > 0) { /* Note: to avoid slow divisions, we do not divide by * 'num_observations', but rather do all math with the numbers * multiplied by 'num_observations'. */ u32 total_delta = 0; for (int i = 0; i < NUM_OBSERVATION_TYPES; i++) { u32 expected = stats->observations[i] * stats->num_new_observations; u32 actual = stats->new_observations[i] * stats->num_observations; u32 delta = (actual > expected) ? actual - expected : expected - actual; total_delta += delta; } /* Ready to end the block? */ if (total_delta >= stats->num_new_observations * 7 / 8 * stats->num_observations) return true; } for (int i = 0; i < NUM_OBSERVATION_TYPES; i++) { stats->num_observations += stats->new_observations[i]; stats->observations[i] += stats->new_observations[i]; stats->new_observations[i] = 0; } stats->num_new_observations = 0; return false; } /******************************************************************************/ /* Slower ("near-optimal") compression algorithm */ /*----------------------------------------------------------------------------*/ /* * Least-recently-used queue for match offsets. * * This is represented as a 64-bit integer for efficiency. There are three * offsets of 21 bits each. Bit 64 is garbage. */ struct lzx_lru_queue { u64 R; } _aligned_attribute(8); #define LZX_QUEUE_OFFSET_SHIFT 21 #define LZX_QUEUE_OFFSET_MASK (((u64)1 << LZX_QUEUE_OFFSET_SHIFT) - 1) #define LZX_QUEUE_R0_SHIFT (0 * LZX_QUEUE_OFFSET_SHIFT) #define LZX_QUEUE_R1_SHIFT (1 * LZX_QUEUE_OFFSET_SHIFT) #define LZX_QUEUE_R2_SHIFT (2 * LZX_QUEUE_OFFSET_SHIFT) #define LZX_QUEUE_R0_MASK (LZX_QUEUE_OFFSET_MASK << LZX_QUEUE_R0_SHIFT) #define LZX_QUEUE_R1_MASK (LZX_QUEUE_OFFSET_MASK << LZX_QUEUE_R1_SHIFT) #define LZX_QUEUE_R2_MASK (LZX_QUEUE_OFFSET_MASK << LZX_QUEUE_R2_SHIFT) #define LZX_QUEUE_INITIALIZER { \ ((u64)1 << LZX_QUEUE_R0_SHIFT) | \ ((u64)1 << LZX_QUEUE_R1_SHIFT) | \ ((u64)1 << LZX_QUEUE_R2_SHIFT) } static forceinline u64 lzx_lru_queue_R0(struct lzx_lru_queue queue) { return (queue.R >> LZX_QUEUE_R0_SHIFT) & LZX_QUEUE_OFFSET_MASK; } static forceinline u64 lzx_lru_queue_R1(struct lzx_lru_queue queue) { return (queue.R >> LZX_QUEUE_R1_SHIFT) & LZX_QUEUE_OFFSET_MASK; } static forceinline u64 lzx_lru_queue_R2(struct lzx_lru_queue queue) { return (queue.R >> LZX_QUEUE_R2_SHIFT) & LZX_QUEUE_OFFSET_MASK; } /* Push a match offset onto the front (most recently used) end of the queue. */ static forceinline struct lzx_lru_queue lzx_lru_queue_push(struct lzx_lru_queue queue, u32 offset) { return (struct lzx_lru_queue) { .R = (queue.R << LZX_QUEUE_OFFSET_SHIFT) | offset, }; } /* Swap a match offset to the front of the queue. */ static forceinline struct lzx_lru_queue lzx_lru_queue_swap(struct lzx_lru_queue queue, unsigned idx) { unsigned shift = idx * 21; const u64 mask = LZX_QUEUE_R0_MASK; const u64 mask_high = mask << shift; return (struct lzx_lru_queue) { (queue.R & ~(mask | mask_high)) | ((queue.R & mask_high) >> shift) | ((queue.R & mask) << shift) }; } static forceinline u32 lzx_walk_item_list(struct lzx_compressor *c, u32 block_size, bool is_16_bit, bool record) { struct lzx_sequence *seq = &c->chosen_sequences[ARRAY_LEN(c->chosen_sequences) - 1]; u32 node_idx = block_size; u32 litrun_end; /* if record=true: end of the current literal run */ if (record) { /* The last sequence has matchlen 0 */ seq->litrunlen_and_matchlen = 0; litrun_end = node_idx; } for (;;) { u32 item; unsigned matchlen; u32 adjusted_offset; unsigned mainsym; /* Tally literals until either a match or the beginning of the * block is reached. Note: the item in the node at the * beginning of the block (c->optimum_nodes[0]) has all bits * set, causing this loop to end when it is reached. */ for (;;) { item = c->optimum_nodes[node_idx].item; if (item & OPTIMUM_LEN_MASK) break; c->freqs.main[item >> OPTIMUM_OFFSET_SHIFT]++; node_idx--; } #if CONSIDER_GAP_MATCHES if (item & OPTIMUM_GAP_MATCH) { if (node_idx == 0) break; /* Tally/record the rep0 match after the gap. */ matchlen = item & OPTIMUM_LEN_MASK; mainsym = lzx_tally_main_and_lensyms(c, matchlen, 0, is_16_bit); if (record) { seq->litrunlen_and_matchlen |= (litrun_end - node_idx) << SEQ_MATCHLEN_BITS; seq--; seq->litrunlen_and_matchlen = matchlen; seq->adjusted_offset_and_mainsym = mainsym; litrun_end = node_idx - matchlen; } /* Tally the literal in the gap. */ c->freqs.main[(u8)(item >> OPTIMUM_OFFSET_SHIFT)]++; /* Fall through and tally the match before the gap. * (It was temporarily saved in the 'cost' field of the * previous node, which was free to reuse.) */ item = c->optimum_nodes[--node_idx].cost; node_idx -= matchlen; } #else /* CONSIDER_GAP_MATCHES */ if (node_idx == 0) break; #endif /* !CONSIDER_GAP_MATCHES */ /* Tally/record a match. */ matchlen = item & OPTIMUM_LEN_MASK; adjusted_offset = item >> OPTIMUM_OFFSET_SHIFT; mainsym = lzx_tally_main_and_lensyms(c, matchlen, adjusted_offset, is_16_bit); if (adjusted_offset >= LZX_MIN_ALIGNED_OFFSET + LZX_OFFSET_ADJUSTMENT) c->freqs.aligned[adjusted_offset & LZX_ALIGNED_OFFSET_BITMASK]++; if (record) { seq->litrunlen_and_matchlen |= (litrun_end - node_idx) << SEQ_MATCHLEN_BITS; seq--; seq->litrunlen_and_matchlen = matchlen; seq->adjusted_offset_and_mainsym = (adjusted_offset << SEQ_MAINSYM_BITS) | mainsym; litrun_end = node_idx - matchlen; } node_idx -= matchlen; } /* Record the literal run length for the first sequence. */ if (record) { seq->litrunlen_and_matchlen |= (litrun_end - node_idx) << SEQ_MATCHLEN_BITS; } /* Return the index in chosen_sequences at which the sequences begin. */ return seq - &c->chosen_sequences[0]; } /* * Given the minimum-cost path computed through the item graph for the current * block, walk the path and count how many of each symbol in each Huffman-coded * alphabet would be required to output the items (matches and literals) along * the path. * * Note that the path will be walked backwards (from the end of the block to the * beginning of the block), but this doesn't matter because this function only * computes frequencies. */ static forceinline void lzx_tally_item_list(struct lzx_compressor *c, u32 block_size, bool is_16_bit) { lzx_walk_item_list(c, block_size, is_16_bit, false); } /* * Like lzx_tally_item_list(), but this function also generates the list of * lzx_sequences for the minimum-cost path and writes it to c->chosen_sequences, * ready to be output to the bitstream after the Huffman codes are computed. * The lzx_sequences will be written to decreasing memory addresses as the path * is walked backwards, which means they will end up in the expected * first-to-last order. The return value is the index in c->chosen_sequences at * which the lzx_sequences begin. */ static forceinline u32 lzx_record_item_list(struct lzx_compressor *c, u32 block_size, bool is_16_bit) { return lzx_walk_item_list(c, block_size, is_16_bit, true); } /* * Find an inexpensive path through the graph of possible match/literal choices * for the current block. The nodes of the graph are * c->optimum_nodes[0...block_size]. They correspond directly to the bytes in * the current block, plus one extra node for end-of-block. The edges of the * graph are matches and literals. The goal is to find the minimum cost path * from 'c->optimum_nodes[0]' to 'c->optimum_nodes[block_size]', given the cost * model 'c->costs'. * * The algorithm works forwards, starting at 'c->optimum_nodes[0]' and * proceeding forwards one node at a time. At each node, a selection of matches * (len >= 2), as well as the literal byte (len = 1), is considered. An item of * length 'len' provides a new path to reach the node 'len' bytes later. If * such a path is the lowest cost found so far to reach that later node, then * that later node is updated with the new cost and the "arrival" which provided * that cost. * * Note that although this algorithm is based on minimum cost path search, due * to various simplifying assumptions the result is not guaranteed to be the * true minimum cost, or "optimal", path over the graph of all valid LZX * representations of this block. * * Also, note that because of the presence of the recent offsets queue (which is * a type of adaptive state), the algorithm cannot work backwards and compute * "cost to end" instead of "cost to beginning". Furthermore, the way the * algorithm handles this adaptive state in the "minimum cost" parse is actually * only an approximation. It's possible for the globally optimal, minimum cost * path to contain a prefix, ending at a position, where that path prefix is * *not* the minimum cost path to that position. This can happen if such a path * prefix results in a different adaptive state which results in lower costs * later. The algorithm does not solve this problem in general; it only looks * one step ahead, with the exception of special consideration for "gap * matches". */ static forceinline struct lzx_lru_queue lzx_find_min_cost_path(struct lzx_compressor * const restrict c, const u8 * const restrict block_begin, const u32 block_size, const struct lzx_lru_queue initial_queue, bool is_16_bit) { struct lzx_optimum_node *cur_node = c->optimum_nodes; struct lzx_optimum_node * const end_node = cur_node + block_size; struct lz_match *cache_ptr = c->match_cache; const u8 *in_next = block_begin; const u8 * const block_end = block_begin + block_size; /* * Instead of storing the match offset LRU queues in the * 'lzx_optimum_node' structures, we save memory (and cache lines) by * storing them in a smaller array. This works because the algorithm * only requires a limited history of the adaptive state. Once a given * state is more than LZX_MAX_MATCH_LEN bytes behind the current node * (more if gap match consideration is enabled; we just round up to 512 * so it's a power of 2), it is no longer needed. * * The QUEUE() macro finds the queue for the given node. This macro has * been optimized by taking advantage of 'struct lzx_lru_queue' and * 'struct lzx_optimum_node' both being 8 bytes in size and alignment. */ struct lzx_lru_queue queues[512]; STATIC_ASSERT(ARRAY_LEN(queues) >= LZX_MAX_MATCH_LEN + 1); STATIC_ASSERT(sizeof(c->optimum_nodes[0]) == sizeof(queues[0])); #define QUEUE(node) \ (*(struct lzx_lru_queue *)((char *)queues + \ ((uintptr_t)(node) % (ARRAY_LEN(queues) * sizeof(queues[0]))))) /*(queues[(uintptr_t)(node) / sizeof(*(node)) % ARRAY_LEN(queues)])*/ #if CONSIDER_GAP_MATCHES u32 matches_before_gap[ARRAY_LEN(queues)]; #define MATCH_BEFORE_GAP(node) \ (matches_before_gap[(uintptr_t)(node) / sizeof(*(node)) % \ ARRAY_LEN(matches_before_gap)]) #endif /* * Initially, the cost to reach each node is "infinity". * * The first node actually should have cost 0, but "infinity" * (0xFFFFFFFF) works just as well because it immediately overflows. * * The following statement also intentionally sets the 'item' of the * first node, which would otherwise have no meaning, to 0xFFFFFFFF for * use as a sentinel. See lzx_walk_item_list(). */ memset(c->optimum_nodes, 0xFF, (block_size + 1) * sizeof(c->optimum_nodes[0])); /* Initialize the recent offsets queue for the first node. */ QUEUE(cur_node) = initial_queue; do { /* For each node in the block in position order... */ unsigned num_matches; unsigned literal; u32 cost; /* * A selection of matches for the block was already saved in * memory so that we don't have to run the uncompressed data * through the matchfinder on every optimization pass. However, * we still search for repeat offset matches during each * optimization pass because we cannot predict the state of the * recent offsets queue. But as a heuristic, we don't bother * searching for repeat offset matches if the general-purpose * matchfinder failed to find any matches. * * Note that a match of length n at some offset implies there is * also a match of length l for LZX_MIN_MATCH_LEN <= l <= n at * that same offset. In other words, we don't necessarily need * to use the full length of a match. The key heuristic that * saves a significicant amount of time is that for each * distinct length, we only consider the smallest offset for * which that length is available. This heuristic also applies * to repeat offsets, which we order specially: R0 < R1 < R2 < * any explicit offset. Of course, this heuristic may be * produce suboptimal results because offset slots in LZX are * subject to entropy encoding, but in practice this is a useful * heuristic. */ num_matches = cache_ptr->length; cache_ptr++; if (num_matches) { struct lz_match *end_matches = cache_ptr + num_matches; unsigned next_len = LZX_MIN_MATCH_LEN; unsigned max_len = min(block_end - in_next, LZX_MAX_MATCH_LEN); const u8 *matchptr; /* Consider rep0 matches. */ matchptr = in_next - lzx_lru_queue_R0(QUEUE(cur_node)); if (load_u16_unaligned(matchptr) != load_u16_unaligned(in_next)) goto rep0_done; STATIC_ASSERT(LZX_MIN_MATCH_LEN == 2); do { u32 cost = cur_node->cost + c->costs.match_cost[0][ next_len - LZX_MIN_MATCH_LEN]; if (cost <= (cur_node + next_len)->cost) { (cur_node + next_len)->cost = cost; (cur_node + next_len)->item = (0 << OPTIMUM_OFFSET_SHIFT) | next_len; } if (unlikely(++next_len > max_len)) { cache_ptr = end_matches; goto done_matches; } } while (in_next[next_len - 1] == matchptr[next_len - 1]); rep0_done: /* Consider rep1 matches. */ matchptr = in_next - lzx_lru_queue_R1(QUEUE(cur_node)); if (load_u16_unaligned(matchptr) != load_u16_unaligned(in_next)) goto rep1_done; if (matchptr[next_len - 1] != in_next[next_len - 1]) goto rep1_done; for (unsigned len = 2; len < next_len - 1; len++) if (matchptr[len] != in_next[len]) goto rep1_done; do { u32 cost = cur_node->cost + c->costs.match_cost[1][ next_len - LZX_MIN_MATCH_LEN]; if (cost <= (cur_node + next_len)->cost) { (cur_node + next_len)->cost = cost; (cur_node + next_len)->item = (1 << OPTIMUM_OFFSET_SHIFT) | next_len; } if (unlikely(++next_len > max_len)) { cache_ptr = end_matches; goto done_matches; } } while (in_next[next_len - 1] == matchptr[next_len - 1]); rep1_done: /* Consider rep2 matches. */ matchptr = in_next - lzx_lru_queue_R2(QUEUE(cur_node)); if (load_u16_unaligned(matchptr) != load_u16_unaligned(in_next)) goto rep2_done; if (matchptr[next_len - 1] != in_next[next_len - 1]) goto rep2_done; for (unsigned len = 2; len < next_len - 1; len++) if (matchptr[len] != in_next[len]) goto rep2_done; do { u32 cost = cur_node->cost + c->costs.match_cost[2][ next_len - LZX_MIN_MATCH_LEN]; if (cost <= (cur_node + next_len)->cost) { (cur_node + next_len)->cost = cost; (cur_node + next_len)->item = (2 << OPTIMUM_OFFSET_SHIFT) | next_len; } if (unlikely(++next_len > max_len)) { cache_ptr = end_matches; goto done_matches; } } while (in_next[next_len - 1] == matchptr[next_len - 1]); rep2_done: while (next_len > cache_ptr->length) if (++cache_ptr == end_matches) goto done_matches; /* Consider explicit offset matches. */ for (;;) { u32 offset = cache_ptr->offset; u32 adjusted_offset = offset + LZX_OFFSET_ADJUSTMENT; unsigned offset_slot = lzx_get_offset_slot(c, adjusted_offset, is_16_bit); u32 base_cost = cur_node->cost; u32 cost; #if CONSIDER_ALIGNED_COSTS if (offset >= LZX_MIN_ALIGNED_OFFSET) base_cost += c->costs.aligned[adjusted_offset & LZX_ALIGNED_OFFSET_BITMASK]; #endif do { cost = base_cost + c->costs.match_cost[offset_slot][ next_len - LZX_MIN_MATCH_LEN]; if (cost < (cur_node + next_len)->cost) { (cur_node + next_len)->cost = cost; (cur_node + next_len)->item = (adjusted_offset << OPTIMUM_OFFSET_SHIFT) | next_len; } } while (++next_len <= cache_ptr->length); if (++cache_ptr == end_matches) { #if CONSIDER_GAP_MATCHES /* Also consider the longest explicit * offset match as a "gap match": match * + lit + rep0. */ s32 remaining = (block_end - in_next) - (s32)next_len; if (likely(remaining >= 2)) { const u8 *strptr = in_next + next_len; const u8 *matchptr = strptr - offset; if (load_u16_unaligned(strptr) == load_u16_unaligned(matchptr)) { STATIC_ASSERT(ARRAY_LEN(queues) - LZX_MAX_MATCH_LEN - 2 >= 250); STATIC_ASSERT(ARRAY_LEN(queues) == ARRAY_LEN(matches_before_gap)); unsigned limit = min(remaining, min(ARRAY_LEN(queues) - LZX_MAX_MATCH_LEN - 2, LZX_MAX_MATCH_LEN)); unsigned rep0_len = lz_extend(strptr, matchptr, 2, limit); u8 lit = strptr[-1]; cost += c->costs.main[lit] + c->costs.match_cost[0][rep0_len - LZX_MIN_MATCH_LEN]; unsigned total_len = next_len + rep0_len; if (cost < (cur_node + total_len)->cost) { (cur_node + total_len)->cost = cost; (cur_node + total_len)->item = OPTIMUM_GAP_MATCH | ((u32)lit << OPTIMUM_OFFSET_SHIFT) | rep0_len; MATCH_BEFORE_GAP(cur_node + total_len) = (adjusted_offset << OPTIMUM_OFFSET_SHIFT) | (next_len - 1); } } } #endif /* CONSIDER_GAP_MATCHES */ break; } } } done_matches: /* Consider coding a literal. * To avoid an extra branch, actually checking the preferability * of coding the literal is integrated into the queue update * code below. */ literal = *in_next++; cost = cur_node->cost + c->costs.main[literal]; /* Advance to the next position. */ cur_node++; /* The lowest-cost path to the current position is now known. * Finalize the recent offsets queue that results from taking * this lowest-cost path. */ if (cost <= cur_node->cost) { /* Literal: queue remains unchanged. */ cur_node->cost = cost; cur_node->item = (u32)literal << OPTIMUM_OFFSET_SHIFT; QUEUE(cur_node) = QUEUE(cur_node - 1); } else { /* Match: queue update is needed. */ unsigned len = cur_node->item & OPTIMUM_LEN_MASK; #if CONSIDER_GAP_MATCHES s32 adjusted_offset = (s32)cur_node->item >> OPTIMUM_OFFSET_SHIFT; STATIC_ASSERT(OPTIMUM_GAP_MATCH == 0x80000000); /* assuming sign extension */ #else u32 adjusted_offset = cur_node->item >> OPTIMUM_OFFSET_SHIFT; #endif if (adjusted_offset >= LZX_NUM_RECENT_OFFSETS) { /* Explicit offset match: insert offset at front. */ QUEUE(cur_node) = lzx_lru_queue_push(QUEUE(cur_node - len), adjusted_offset - LZX_OFFSET_ADJUSTMENT); } #if CONSIDER_GAP_MATCHES else if (adjusted_offset < 0) { /* "Gap match": Explicit offset match, then a * literal, then rep0 match. Save the explicit * offset match information in the cost field of * the previous node, which isn't needed * anymore. Then insert the offset at the front * of the queue. */ u32 match_before_gap = MATCH_BEFORE_GAP(cur_node); (cur_node - 1)->cost = match_before_gap; QUEUE(cur_node) = lzx_lru_queue_push(QUEUE(cur_node - len - 1 - (match_before_gap & OPTIMUM_LEN_MASK)), (match_before_gap >> OPTIMUM_OFFSET_SHIFT) - LZX_OFFSET_ADJUSTMENT); } #endif else { /* Repeat offset match: swap offset to front. */ QUEUE(cur_node) = lzx_lru_queue_swap(QUEUE(cur_node - len), adjusted_offset); } } } while (cur_node != end_node); /* Return the recent offsets queue at the end of the path. */ return QUEUE(cur_node); } /* * Given the costs for the main and length codewords (c->costs.main and * c->costs.len), initialize the match cost array (c->costs.match_cost) which * directly provides the cost of every possible (length, offset slot) pair. */ static void lzx_compute_match_costs(struct lzx_compressor *c) { unsigned num_offset_slots = (c->num_main_syms - LZX_NUM_CHARS) / LZX_NUM_LEN_HEADERS; struct lzx_costs *costs = &c->costs; unsigned main_symbol = LZX_NUM_CHARS; for (unsigned offset_slot = 0; offset_slot < num_offset_slots; offset_slot++) { u32 extra_cost = lzx_extra_offset_bits[offset_slot] * BIT_COST; unsigned i; #if CONSIDER_ALIGNED_COSTS if (offset_slot >= LZX_MIN_ALIGNED_OFFSET_SLOT) extra_cost -= LZX_NUM_ALIGNED_OFFSET_BITS * BIT_COST; #endif for (i = 0; i < LZX_NUM_PRIMARY_LENS; i++) { costs->match_cost[offset_slot][i] = costs->main[main_symbol++] + extra_cost; } extra_cost += costs->main[main_symbol++]; for (; i < LZX_NUM_LENS; i++) { costs->match_cost[offset_slot][i] = costs->len[i - LZX_NUM_PRIMARY_LENS] + extra_cost; } } } /* * Fast approximation for log2f(x). This is not as accurate as the standard C * version. It does not need to be perfectly accurate because it is only used * for estimating symbol costs, which is very approximate anyway. */ static float log2f_fast(float x) { union { float f; s32 i; } u = { .f = x }; /* Extract the exponent and subtract 127 to remove the bias. This gives * the integer part of the result. */ float res = ((u.i >> 23) & 0xFF) - 127; /* Set the exponent to 0 (plus bias of 127). This transforms the number * to the range [1, 2) while retaining the same mantissa. */ u.i = (u.i & ~(0xFF << 23)) | (127 << 23); /* * Approximate the log2 of the transformed number using a degree 2 * interpolating polynomial for log2(x) over the interval [1, 2). Then * add this to the extracted exponent to produce the final approximation * of log2(x). * * The coefficients of the interpolating polynomial used here were found * using the script tools/log2_interpolation.r. */ return res - 1.653124006f + u.f * (1.9941812f - u.f * 0.3347490189f); } /* * Return the estimated cost of a symbol which has been estimated to have the * given probability. */ static u32 lzx_cost_for_probability(float prob) { /* * The basic formula is: * * entropy = -log2(probability) * * Use this to get the cost in fractional bits. Then multiply by our * scaling factor of BIT_COST and convert to an integer. * * In addition, the minimum cost is BIT_COST (one bit) because the * entropy coding method will be Huffman codes. * * Careful: even though 'prob' should be <= 1.0, 'log2f_fast(prob)' may * be positive due to inaccuracy in our log2 approximation. Therefore, * we cannot, in general, assume the computed cost is non-negative, and * we should make sure negative costs get rounded up correctly. */ s32 cost = -log2f_fast(prob) * BIT_COST; return max(cost, BIT_COST); } /* * Mapping: number of used literals => heuristic probability of a literal times * 6870. Generated by running this R command: * * cat(paste(round(6870*2^-((304+(0:256))/64)), collapse=", ")) */ static const u8 literal_scaled_probs[257] = { 255, 253, 250, 247, 244, 242, 239, 237, 234, 232, 229, 227, 224, 222, 219, 217, 215, 212, 210, 208, 206, 203, 201, 199, 197, 195, 193, 191, 189, 186, 184, 182, 181, 179, 177, 175, 173, 171, 169, 167, 166, 164, 162, 160, 159, 157, 155, 153, 152, 150, 149, 147, 145, 144, 142, 141, 139, 138, 136, 135, 133, 132, 130, 129, 128, 126, 125, 124, 122, 121, 120, 118, 117, 116, 115, 113, 112, 111, 110, 109, 107, 106, 105, 104, 103, 102, 101, 100, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 86, 85, 84, 83, 82, 81, 80, 79, 78, 78, 77, 76, 75, 74, 73, 73, 72, 71, 70, 70, 69, 68, 67, 67, 66, 65, 65, 64, 63, 62, 62, 61, 60, 60, 59, 59, 58, 57, 57, 56, 55, 55, 54, 54, 53, 53, 52, 51, 51, 50, 50, 49, 49, 48, 48, 47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 42, 42, 41, 41, 40, 40, 40, 39, 39, 38, 38, 38, 37, 37, 36, 36, 36, 35, 35, 34, 34, 34, 33, 33, 33, 32, 32, 32, 31, 31, 31, 30, 30, 30, 29, 29, 29, 28, 28, 28, 27, 27, 27, 27, 26, 26, 26, 25, 25, 25, 25, 24, 24, 24, 24, 23, 23, 23, 23, 22, 22, 22, 22, 21, 21, 21, 21, 20, 20, 20, 20, 20, 19, 19, 19, 19, 19, 18, 18, 18, 18, 18, 17, 17, 17, 17, 17, 16, 16, 16, 16 }; /* * Mapping: length symbol => default cost of that symbol. This is derived from * sample data but has been slightly edited to add more bias towards the * shortest lengths, which are the most common. */ static const u16 lzx_default_len_costs[LZX_LENCODE_NUM_SYMBOLS] = { 300, 310, 320, 330, 360, 396, 399, 416, 451, 448, 463, 466, 505, 492, 503, 514, 547, 531, 566, 561, 589, 563, 592, 586, 623, 602, 639, 627, 659, 643, 657, 650, 685, 662, 661, 672, 685, 686, 696, 680, 657, 682, 666, 699, 674, 699, 679, 709, 688, 712, 692, 714, 694, 716, 698, 712, 706, 727, 714, 727, 713, 723, 712, 718, 719, 719, 720, 735, 725, 735, 728, 740, 727, 739, 727, 742, 716, 733, 733, 740, 738, 746, 737, 747, 738, 745, 736, 748, 742, 749, 745, 749, 743, 748, 741, 752, 745, 752, 747, 750, 747, 752, 748, 753, 750, 752, 753, 753, 749, 744, 752, 755, 753, 756, 745, 748, 746, 745, 723, 757, 755, 758, 755, 758, 752, 757, 754, 757, 755, 759, 755, 758, 753, 755, 755, 758, 757, 761, 755, 750, 758, 759, 759, 760, 758, 751, 757, 757, 759, 759, 758, 759, 758, 761, 750, 761, 758, 760, 759, 761, 758, 761, 760, 752, 759, 760, 759, 759, 757, 762, 760, 761, 761, 748, 761, 760, 762, 763, 752, 762, 762, 763, 762, 762, 763, 763, 762, 763, 762, 763, 762, 763, 763, 764, 763, 762, 763, 762, 762, 762, 764, 764, 763, 764, 763, 763, 763, 762, 763, 763, 762, 764, 764, 763, 762, 763, 763, 763, 763, 762, 764, 763, 762, 764, 764, 763, 763, 765, 764, 764, 762, 763, 764, 765, 763, 764, 763, 764, 762, 764, 764, 754, 763, 764, 763, 763, 762, 763, 584, }; /* Set default costs to bootstrap the iterative optimization algorithm. */ static void lzx_set_default_costs(struct lzx_compressor *c) { unsigned i; u32 num_literals = 0; u32 num_used_literals = 0; float inv_num_matches = 1.0f / c->freqs.main[LZX_NUM_CHARS]; float inv_num_items; float prob_match = 1.0f; u32 match_cost; float base_literal_prob; /* Some numbers here have been hardcoded to assume a bit cost of 64. */ STATIC_ASSERT(BIT_COST == 64); /* Estimate the number of literals that will used. 'num_literals' is * the total number, whereas 'num_used_literals' is the number of * distinct symbols. */ for (i = 0; i < LZX_NUM_CHARS; i++) { num_literals += c->freqs.main[i]; num_used_literals += (c->freqs.main[i] != 0); } /* Note: all match headers were tallied as symbol 'LZX_NUM_CHARS'. We * don't attempt to estimate which ones will be used. */ inv_num_items = 1.0f / (num_literals + c->freqs.main[LZX_NUM_CHARS]); base_literal_prob = literal_scaled_probs[num_used_literals] * (1.0f / 6870.0f); /* Literal costs. We use two different methods to compute the * probability of each literal and mix together their results. */ for (i = 0; i < LZX_NUM_CHARS; i++) { u32 freq = c->freqs.main[i]; if (freq != 0) { float prob = 0.5f * ((freq * inv_num_items) + base_literal_prob); c->costs.main[i] = lzx_cost_for_probability(prob); prob_match -= prob; } else { c->costs.main[i] = 11 * BIT_COST; } } /* Match header costs. We just assume that all match headers are * equally probable, but we do take into account the relative cost of a * match header vs. a literal depending on how common matches are * expected to be vs. literals. */ prob_match = max(prob_match, 0.15f); match_cost = lzx_cost_for_probability(prob_match / (c->num_main_syms - LZX_NUM_CHARS)); for (; i < c->num_main_syms; i++) c->costs.main[i] = match_cost; /* Length symbol costs. These are just set to fixed values which * reflect the fact the smallest lengths are typically the most common, * and therefore are typically the cheapest. */ for (i = 0; i < LZX_LENCODE_NUM_SYMBOLS; i++) c->costs.len[i] = lzx_default_len_costs[i]; #if CONSIDER_ALIGNED_COSTS /* Aligned offset symbol costs. These are derived from the estimated * probability of each aligned offset symbol. */ for (i = 0; i < LZX_ALIGNEDCODE_NUM_SYMBOLS; i++) { /* We intentionally tallied the frequencies in the wrong slots, * not accounting for LZX_OFFSET_ADJUSTMENT, since doing the * fixup here is faster: a constant 8 subtractions here vs. one * addition for every match. */ unsigned j = (i - LZX_OFFSET_ADJUSTMENT) & LZX_ALIGNED_OFFSET_BITMASK; if (c->freqs.aligned[j] != 0) { float prob = c->freqs.aligned[j] * inv_num_matches; c->costs.aligned[i] = lzx_cost_for_probability(prob); } else { c->costs.aligned[i] = (2 * LZX_NUM_ALIGNED_OFFSET_BITS) * BIT_COST; } } #endif } /* Update the current cost model to reflect the computed Huffman codes. */ static void lzx_set_costs_from_codes(struct lzx_compressor *c) { unsigned i; const struct lzx_lens *lens = &c->codes[c->codes_index].lens; for (i = 0; i < c->num_main_syms; i++) { c->costs.main[i] = (lens->main[i] ? lens->main[i] : MAIN_CODEWORD_LIMIT) * BIT_COST; } for (i = 0; i < LZX_LENCODE_NUM_SYMBOLS; i++) { c->costs.len[i] = (lens->len[i] ? lens->len[i] : LENGTH_CODEWORD_LIMIT) * BIT_COST; } #if CONSIDER_ALIGNED_COSTS for (i = 0; i < LZX_ALIGNEDCODE_NUM_SYMBOLS; i++) { c->costs.aligned[i] = (lens->aligned[i] ? lens->aligned[i] : ALIGNED_CODEWORD_LIMIT) * BIT_COST; } #endif } /* * Choose a "near-optimal" literal/match sequence to use for the current block, * then flush the block. Because the cost of each Huffman symbol is unknown * until the Huffman codes have been built and the Huffman codes themselves * depend on the symbol frequencies, this uses an iterative optimization * algorithm to approximate an optimal solution. The first optimization pass * for the block uses default costs; additional passes use costs derived from * the Huffman codes computed in the previous pass. */ static forceinline struct lzx_lru_queue lzx_optimize_and_flush_block(struct lzx_compressor * const restrict c, struct lzx_output_bitstream * const restrict os, const u8 * const restrict block_begin, const u32 block_size, const struct lzx_lru_queue initial_queue, bool is_16_bit) { unsigned num_passes_remaining = c->num_optim_passes; struct lzx_lru_queue new_queue; u32 seq_idx; lzx_set_default_costs(c); for (;;) { lzx_compute_match_costs(c); new_queue = lzx_find_min_cost_path(c, block_begin, block_size, initial_queue, is_16_bit); if (--num_passes_remaining == 0) break; /* At least one optimization pass remains. Update the costs. */ lzx_reset_symbol_frequencies(c); lzx_tally_item_list(c, block_size, is_16_bit); lzx_build_huffman_codes(c); lzx_set_costs_from_codes(c); } /* Done optimizing. Generate the sequence list and flush the block. */ lzx_reset_symbol_frequencies(c); seq_idx = lzx_record_item_list(c, block_size, is_16_bit); lzx_flush_block(c, os, block_begin, block_size, seq_idx); return new_queue; } /* * This is the "near-optimal" LZX compressor. * * For each block, it performs a relatively thorough graph search to find an * inexpensive (in terms of compressed size) way to output the block. * * Note: there are actually many things this algorithm leaves on the table in * terms of compression ratio. So although it may be "near-optimal", it is * certainly not "optimal". The goal is not to produce the optimal compression * ratio, which for LZX is probably impossible within any practical amount of * time, but rather to produce a compression ratio significantly better than a * simpler "greedy" or "lazy" parse while still being relatively fast. */ static forceinline void lzx_compress_near_optimal(struct lzx_compressor * restrict c, const u8 * const restrict in_begin, size_t in_nbytes, struct lzx_output_bitstream * restrict os, bool is_16_bit) { const u8 * in_next = in_begin; const u8 * const in_end = in_begin + in_nbytes; u32 max_len = LZX_MAX_MATCH_LEN; u32 nice_len = min(c->nice_match_length, max_len); u32 next_hashes[2] = {0, 0}; struct lzx_lru_queue queue = LZX_QUEUE_INITIALIZER; /* Initialize the matchfinder. */ CALL_BT_MF(is_16_bit, c, bt_matchfinder_init); do { /* Starting a new block */ const u8 * const in_block_begin = in_next; const u8 * const in_max_block_end = in_next + min(SOFT_MAX_BLOCK_SIZE, in_end - in_next); struct lz_match *cache_ptr = c->match_cache; const u8 *next_search_pos = in_next; const u8 *next_observation = in_next; const u8 *next_pause_point = min(in_next + min(MIN_BLOCK_SIZE, in_max_block_end - in_next), in_max_block_end - min(LZX_MAX_MATCH_LEN - 1, in_max_block_end - in_next)); lzx_init_block_split_stats(&c->split_stats); lzx_reset_symbol_frequencies(c); if (in_next >= next_pause_point) goto pause; /* * Run the input buffer through the matchfinder, caching the * matches, until we decide to end the block. * * For a tighter matchfinding loop, we compute a "pause point", * which is the next position at which we may need to check * whether to end the block or to decrease max_len. We then * only do these extra checks upon reaching the pause point. */ resume_matchfinding: do { if (in_next >= next_search_pos) { /* Search for matches at this position. */ struct lz_match *lz_matchptr; u32 best_len; lz_matchptr = CALL_BT_MF(is_16_bit, c, bt_matchfinder_get_matches, in_begin, in_next - in_begin, max_len, nice_len, c->max_search_depth, next_hashes, &best_len, cache_ptr + 1); cache_ptr->length = lz_matchptr - (cache_ptr + 1); cache_ptr = lz_matchptr; /* Accumulate literal/match statistics for block * splitting and for generating the initial cost * model. */ if (in_next >= next_observation) { best_len = cache_ptr[-1].length; if (best_len >= 3) { /* Match (len >= 3) */ /* * Note: for performance reasons this has * been simplified significantly: * * - We wait until later to account for * LZX_OFFSET_ADJUSTMENT. * - We don't account for repeat offsets. * - We don't account for different match headers. */ c->freqs.aligned[cache_ptr[-1].offset & LZX_ALIGNED_OFFSET_BITMASK]++; c->freqs.main[LZX_NUM_CHARS]++; lzx_observe_match(&c->split_stats, best_len); next_observation = in_next + best_len; } else { /* Literal */ c->freqs.main[*in_next]++; lzx_observe_literal(&c->split_stats, *in_next); next_observation = in_next + 1; } } /* * If there was a very long match found, then * don't cache any matches for the bytes covered * by that match. This avoids degenerate * behavior when compressing highly redundant * data, where the number of matches can be very * large. * * This heuristic doesn't actually hurt the * compression ratio *too* much. If there's a * long match, then the data must be highly * compressible, so it doesn't matter as much * what we do. */ if (best_len >= nice_len) next_search_pos = in_next + best_len; } else { /* Don't search for matches at this position. */ CALL_BT_MF(is_16_bit, c, bt_matchfinder_skip_position, in_begin, in_next - in_begin, nice_len, c->max_search_depth, next_hashes); cache_ptr->length = 0; cache_ptr++; } } while (++in_next < next_pause_point && likely(cache_ptr < &c->match_cache[CACHE_LENGTH])); pause: /* Adjust max_len and nice_len if we're nearing the end of the * input buffer. In addition, if we are so close to the end of * the input buffer that there cannot be any more matches, then * just advance through the last few positions and record no * matches. */ if (unlikely(max_len > in_end - in_next)) { max_len = in_end - in_next; nice_len = min(max_len, nice_len); if (max_len < BT_MATCHFINDER_REQUIRED_NBYTES) { while (in_next != in_end) { cache_ptr->length = 0; cache_ptr++; in_next++; } } } /* End the block if the match cache may overflow. */ if (unlikely(cache_ptr >= &c->match_cache[CACHE_LENGTH])) goto end_block; /* End the block if the soft maximum size has been reached. */ if (in_next >= in_max_block_end) goto end_block; /* End the block if the block splitting algorithm thinks this is * a good place to do so. */ if (c->split_stats.num_new_observations >= NUM_OBSERVATIONS_PER_BLOCK_CHECK && in_max_block_end - in_next >= MIN_BLOCK_SIZE && lzx_should_end_block(&c->split_stats)) goto end_block; /* It's not time to end the block yet. Compute the next pause * point and resume matchfinding. */ next_pause_point = min(in_next + min(NUM_OBSERVATIONS_PER_BLOCK_CHECK * 2 - c->split_stats.num_new_observations, in_max_block_end - in_next), in_max_block_end - min(LZX_MAX_MATCH_LEN - 1, in_max_block_end - in_next)); goto resume_matchfinding; end_block: /* We've decided on a block boundary and cached matches. Now * choose a match/literal sequence and flush the block. */ queue = lzx_optimize_and_flush_block(c, os, in_block_begin, in_next - in_block_begin, queue, is_16_bit); } while (in_next != in_end); } static void lzx_compress_near_optimal_16(struct lzx_compressor *c, const u8 *in, size_t in_nbytes, struct lzx_output_bitstream *os) { lzx_compress_near_optimal(c, in, in_nbytes, os, true); } static void lzx_compress_near_optimal_32(struct lzx_compressor *c, const u8 *in, size_t in_nbytes, struct lzx_output_bitstream *os) { lzx_compress_near_optimal(c, in, in_nbytes, os, false); } /******************************************************************************/ /* Faster ("lazy") compression algorithm */ /*----------------------------------------------------------------------------*/ /* * Called when the compressor chooses to use a literal. This tallies the * Huffman symbol for the literal, increments the current literal run length, * and "observes" the literal for the block split statistics. */ static forceinline void lzx_choose_literal(struct lzx_compressor *c, unsigned literal, u32 *litrunlen_p) { lzx_observe_literal(&c->split_stats, literal); c->freqs.main[literal]++; ++*litrunlen_p; } /* * Called when the compressor chooses to use a match. This tallies the Huffman * symbol(s) for a match, saves the match data and the length of the preceding * literal run, updates the recent offsets queue, and "observes" the match for * the block split statistics. */ static forceinline void lzx_choose_match(struct lzx_compressor *c, unsigned length, u32 adjusted_offset, u32 recent_offsets[LZX_NUM_RECENT_OFFSETS], bool is_16_bit, u32 *litrunlen_p, struct lzx_sequence **next_seq_p) { struct lzx_sequence *next_seq = *next_seq_p; unsigned mainsym; lzx_observe_match(&c->split_stats, length); mainsym = lzx_tally_main_and_lensyms(c, length, adjusted_offset, is_16_bit); next_seq->litrunlen_and_matchlen = (*litrunlen_p << SEQ_MATCHLEN_BITS) | length; next_seq->adjusted_offset_and_mainsym = (adjusted_offset << SEQ_MAINSYM_BITS) | mainsym; /* Update the recent offsets queue. */ if (adjusted_offset < LZX_NUM_RECENT_OFFSETS) { /* Repeat offset match. */ swap(recent_offsets[0], recent_offsets[adjusted_offset]); } else { /* Explicit offset match. */ /* Tally the aligned offset symbol if needed. */ if (adjusted_offset >= LZX_MIN_ALIGNED_OFFSET + LZX_OFFSET_ADJUSTMENT) c->freqs.aligned[adjusted_offset & LZX_ALIGNED_OFFSET_BITMASK]++; recent_offsets[2] = recent_offsets[1]; recent_offsets[1] = recent_offsets[0]; recent_offsets[0] = adjusted_offset - LZX_OFFSET_ADJUSTMENT; } /* Reset the literal run length and advance to the next sequence. */ *next_seq_p = next_seq + 1; *litrunlen_p = 0; } /* * Called when the compressor ends a block. This finshes the last lzx_sequence, * which is just a literal run with no following match. This literal run might * be empty. */ static forceinline void lzx_finish_sequence(struct lzx_sequence *last_seq, u32 litrunlen) { last_seq->litrunlen_and_matchlen = litrunlen << SEQ_MATCHLEN_BITS; } /* * Find the longest repeat offset match with the current position. If a match * is found, return its length and set *best_rep_idx_ret to the index of its * offset in @recent_offsets. Otherwise, return 0. * * Don't bother with length 2 matches; consider matches of length >= 3 only. * Also assume that max_len >= 3. */ static unsigned lzx_find_longest_repeat_offset_match(const u8 * const in_next, const u32 recent_offsets[], const unsigned max_len, unsigned *best_rep_idx_ret) { STATIC_ASSERT(LZX_NUM_RECENT_OFFSETS == 3); /* loop is unrolled */ const u32 seq3 = load_u24_unaligned(in_next); const u8 *matchptr; unsigned best_rep_len = 0; unsigned best_rep_idx = 0; unsigned rep_len; /* Check for rep0 match (most recent offset) */ matchptr = in_next - recent_offsets[0]; if (load_u24_unaligned(matchptr) == seq3) best_rep_len = lz_extend(in_next, matchptr, 3, max_len); /* Check for rep1 match (second most recent offset) */ matchptr = in_next - recent_offsets[1]; if (load_u24_unaligned(matchptr) == seq3) { rep_len = lz_extend(in_next, matchptr, 3, max_len); if (rep_len > best_rep_len) { best_rep_len = rep_len; best_rep_idx = 1; } } /* Check for rep2 match (third most recent offset) */ matchptr = in_next - recent_offsets[2]; if (load_u24_unaligned(matchptr) == seq3) { rep_len = lz_extend(in_next, matchptr, 3, max_len); if (rep_len > best_rep_len) { best_rep_len = rep_len; best_rep_idx = 2; } } *best_rep_idx_ret = best_rep_idx; return best_rep_len; } /* * Fast heuristic scoring for lazy parsing: how "good" is this match? * This is mainly determined by the length: longer matches are better. * However, we also give a bonus to close (small offset) matches and to repeat * offset matches, since those require fewer bits to encode. */ static forceinline unsigned lzx_explicit_offset_match_score(unsigned len, u32 adjusted_offset) { unsigned score = len; if (adjusted_offset < 4096) score++; if (adjusted_offset < 256) score++; return score; } static forceinline unsigned lzx_repeat_offset_match_score(unsigned rep_len, unsigned rep_idx) { return rep_len + 3; } /* * This is the "lazy" LZX compressor. The basic idea is that before it chooses * a match, it checks to see if there's a longer match at the next position. If * yes, it chooses a literal and continues to the next position. If no, it * chooses the match. * * Some additional heuristics are used as well. Repeat offset matches are * considered favorably and sometimes are chosen immediately. In addition, long * matches (at least "nice_len" bytes) are chosen immediately as well. Finally, * when we decide whether a match is "better" than another, we take the offset * into consideration as well as the length. */ static forceinline void lzx_compress_lazy(struct lzx_compressor * restrict c, const u8 * const restrict in_begin, size_t in_nbytes, struct lzx_output_bitstream * restrict os, bool is_16_bit) { const u8 * in_next = in_begin; const u8 * const in_end = in_begin + in_nbytes; unsigned max_len = LZX_MAX_MATCH_LEN; unsigned nice_len = min(c->nice_match_length, max_len); STATIC_ASSERT(LZX_NUM_RECENT_OFFSETS == 3); u32 recent_offsets[LZX_NUM_RECENT_OFFSETS] = {1, 1, 1}; u32 next_hashes[2] = {0, 0}; /* Initialize the matchfinder. */ CALL_HC_MF(is_16_bit, c, hc_matchfinder_init); do { /* Starting a new block */ const u8 * const in_block_begin = in_next; const u8 * const in_max_block_end = in_next + min(SOFT_MAX_BLOCK_SIZE, in_end - in_next); struct lzx_sequence *next_seq = c->chosen_sequences; u32 litrunlen = 0; unsigned cur_len; u32 cur_offset; u32 cur_adjusted_offset; unsigned cur_score; unsigned next_len; u32 next_offset; u32 next_adjusted_offset; unsigned next_score; unsigned best_rep_len; unsigned best_rep_idx; unsigned rep_score; unsigned skip_len; lzx_reset_symbol_frequencies(c); lzx_init_block_split_stats(&c->split_stats); do { /* Adjust max_len and nice_len if we're nearing the end * of the input buffer. */ if (unlikely(max_len > in_end - in_next)) { max_len = in_end - in_next; nice_len = min(max_len, nice_len); } /* Find the longest match (subject to the * max_search_depth cutoff parameter) with the current * position. Don't bother with length 2 matches; only * look for matches of length >= 3. */ cur_len = CALL_HC_MF(is_16_bit, c, hc_matchfinder_longest_match, in_begin, in_next - in_begin, 2, max_len, nice_len, c->max_search_depth, next_hashes, &cur_offset); /* If there was no match found, or the only match found * was a distant short match, then choose a literal. */ if (cur_len < 3 || (cur_len == 3 && cur_offset >= 8192 - LZX_OFFSET_ADJUSTMENT && cur_offset != recent_offsets[0] && cur_offset != recent_offsets[1] && cur_offset != recent_offsets[2])) { lzx_choose_literal(c, *in_next, &litrunlen); in_next++; continue; } /* Heuristic: if this match has the most recent offset, * then go ahead and choose it as a rep0 match. */ if (cur_offset == recent_offsets[0]) { in_next++; skip_len = cur_len - 1; cur_adjusted_offset = 0; goto choose_cur_match; } /* Compute the longest match's score as an explicit * offset match. */ cur_adjusted_offset = cur_offset + LZX_OFFSET_ADJUSTMENT; cur_score = lzx_explicit_offset_match_score(cur_len, cur_adjusted_offset); /* Find the longest repeat offset match at this * position. If we find one and it's "better" than the * explicit offset match we found, then go ahead and * choose the repeat offset match immediately. */ best_rep_len = lzx_find_longest_repeat_offset_match(in_next, recent_offsets, max_len, &best_rep_idx); in_next++; if (best_rep_len != 0 && (rep_score = lzx_repeat_offset_match_score(best_rep_len, best_rep_idx)) >= cur_score) { cur_len = best_rep_len; cur_adjusted_offset = best_rep_idx; skip_len = best_rep_len - 1; goto choose_cur_match; } have_cur_match: /* * We have a match at the current position. If the * match is very long, then choose it immediately. * Otherwise, see if there's a better match at the next * position. */ if (cur_len >= nice_len) { skip_len = cur_len - 1; goto choose_cur_match; } if (unlikely(max_len > in_end - in_next)) { max_len = in_end - in_next; nice_len = min(max_len, nice_len); } next_len = CALL_HC_MF(is_16_bit, c, hc_matchfinder_longest_match, in_begin, in_next - in_begin, cur_len - 2, max_len, nice_len, c->max_search_depth / 2, next_hashes, &next_offset); if (next_len <= cur_len - 2) { /* No potentially better match was found. */ in_next++; skip_len = cur_len - 2; goto choose_cur_match; } next_adjusted_offset = next_offset + LZX_OFFSET_ADJUSTMENT; next_score = lzx_explicit_offset_match_score(next_len, next_adjusted_offset); best_rep_len = lzx_find_longest_repeat_offset_match(in_next, recent_offsets, max_len, &best_rep_idx); in_next++; if (best_rep_len != 0 && (rep_score = lzx_repeat_offset_match_score(best_rep_len, best_rep_idx)) >= next_score) { if (rep_score > cur_score) { /* The next match is better, and it's a * repeat offset match. */ lzx_choose_literal(c, *(in_next - 2), &litrunlen); cur_len = best_rep_len; cur_adjusted_offset = best_rep_idx; skip_len = cur_len - 1; goto choose_cur_match; } } else { if (next_score > cur_score) { /* The next match is better, and it's an * explicit offset match. */ lzx_choose_literal(c, *(in_next - 2), &litrunlen); cur_len = next_len; cur_adjusted_offset = next_adjusted_offset; cur_score = next_score; goto have_cur_match; } } /* The original match was better; choose it. */ skip_len = cur_len - 2; choose_cur_match: /* Choose a match and have the matchfinder skip over its * remaining bytes. */ lzx_choose_match(c, cur_len, cur_adjusted_offset, recent_offsets, is_16_bit, &litrunlen, &next_seq); in_next = CALL_HC_MF(is_16_bit, c, hc_matchfinder_skip_positions, in_begin, in_next - in_begin, in_end - in_begin, skip_len, next_hashes); /* Keep going until it's time to end the block. */ } while (in_next < in_max_block_end && !(c->split_stats.num_new_observations >= NUM_OBSERVATIONS_PER_BLOCK_CHECK && in_next - in_block_begin >= MIN_BLOCK_SIZE && in_end - in_next >= MIN_BLOCK_SIZE && lzx_should_end_block(&c->split_stats))); /* Flush the block. */ lzx_finish_sequence(next_seq, litrunlen); lzx_flush_block(c, os, in_block_begin, in_next - in_block_begin, 0); /* Keep going until we've reached the end of the input buffer. */ } while (in_next != in_end); } static void lzx_compress_lazy_16(struct lzx_compressor *c, const u8 *in, size_t in_nbytes, struct lzx_output_bitstream *os) { lzx_compress_lazy(c, in, in_nbytes, os, true); } static void lzx_compress_lazy_32(struct lzx_compressor *c, const u8 *in, size_t in_nbytes, struct lzx_output_bitstream *os) { lzx_compress_lazy(c, in, in_nbytes, os, false); } /******************************************************************************/ /* Compressor operations */ /*----------------------------------------------------------------------------*/ /* * Generate tables for mapping match offsets (actually, "adjusted" match * offsets) to offset slots. */ static void lzx_init_offset_slot_tabs(struct lzx_compressor *c) { u32 adjusted_offset = 0; unsigned slot = 0; /* slots [0, 29] */ for (; adjusted_offset < ARRAY_LEN(c->offset_slot_tab_1); adjusted_offset++) { if (adjusted_offset >= lzx_offset_slot_base[slot + 1] + LZX_OFFSET_ADJUSTMENT) slot++; c->offset_slot_tab_1[adjusted_offset] = slot; } /* slots [30, 49] */ for (; adjusted_offset < LZX_MAX_WINDOW_SIZE; adjusted_offset += (u32)1 << 14) { if (adjusted_offset >= lzx_offset_slot_base[slot + 1] + LZX_OFFSET_ADJUSTMENT) slot++; c->offset_slot_tab_2[adjusted_offset >> 14] = slot; } } static size_t lzx_get_compressor_size(size_t max_bufsize, unsigned compression_level) { if (compression_level <= MAX_FAST_LEVEL) { if (lzx_is_16_bit(max_bufsize)) return offsetof(struct lzx_compressor, hc_mf_16) + hc_matchfinder_size_16(max_bufsize); else return offsetof(struct lzx_compressor, hc_mf_32) + hc_matchfinder_size_32(max_bufsize); } else { if (lzx_is_16_bit(max_bufsize)) return offsetof(struct lzx_compressor, bt_mf_16) + bt_matchfinder_size_16(max_bufsize); else return offsetof(struct lzx_compressor, bt_mf_32) + bt_matchfinder_size_32(max_bufsize); } } /* Compute the amount of memory needed to allocate an LZX compressor. */ static u64 lzx_get_needed_memory(size_t max_bufsize, unsigned compression_level, bool destructive) { u64 size = 0; if (max_bufsize > LZX_MAX_WINDOW_SIZE) return 0; size += lzx_get_compressor_size(max_bufsize, compression_level); if (!destructive) size += max_bufsize; /* account for in_buffer */ return size; } /* Allocate an LZX compressor. */ static int lzx_create_compressor(size_t max_bufsize, unsigned compression_level, bool destructive, void **c_ret) { unsigned window_order; struct lzx_compressor *c; /* Validate the maximum buffer size and get the window order from it. */ window_order = lzx_get_window_order(max_bufsize); if (window_order == 0) return WIMLIB_ERR_INVALID_PARAM; /* Allocate the compressor. */ c = MALLOC(lzx_get_compressor_size(max_bufsize, compression_level)); if (!c) goto oom0; c->window_order = window_order; c->num_main_syms = lzx_get_num_main_syms(window_order); c->destructive = destructive; /* Allocate the buffer for preprocessed data if needed. */ if (!c->destructive) { c->in_buffer = MALLOC(max_bufsize); if (!c->in_buffer) goto oom1; } if (compression_level <= MAX_FAST_LEVEL) { /* Fast compression: Use lazy parsing. */ if (lzx_is_16_bit(max_bufsize)) c->impl = lzx_compress_lazy_16; else c->impl = lzx_compress_lazy_32; /* Scale max_search_depth and nice_match_length with the * compression level. */ c->max_search_depth = (60 * compression_level) / 20; c->nice_match_length = (80 * compression_level) / 20; /* lzx_compress_lazy() needs max_search_depth >= 2 because it * halves the max_search_depth when attempting a lazy match, and * max_search_depth must be at least 1. */ c->max_search_depth = max(c->max_search_depth, 2); } else { /* Normal / high compression: Use near-optimal parsing. */ if (lzx_is_16_bit(max_bufsize)) c->impl = lzx_compress_near_optimal_16; else c->impl = lzx_compress_near_optimal_32; /* Scale max_search_depth and nice_match_length with the * compression level. */ c->max_search_depth = (24 * compression_level) / 50; c->nice_match_length = (48 * compression_level) / 50; /* Also scale num_optim_passes with the compression level. But * the more passes there are, the less they help --- so don't * add them linearly. */ c->num_optim_passes = 1; c->num_optim_passes += (compression_level >= 45); c->num_optim_passes += (compression_level >= 70); c->num_optim_passes += (compression_level >= 100); c->num_optim_passes += (compression_level >= 150); c->num_optim_passes += (compression_level >= 200); c->num_optim_passes += (compression_level >= 300); /* max_search_depth must be at least 1. */ c->max_search_depth = max(c->max_search_depth, 1); } /* Prepare the offset => offset slot mapping. */ lzx_init_offset_slot_tabs(c); *c_ret = c; return 0; oom1: FREE(c); oom0: return WIMLIB_ERR_NOMEM; } /* Compress a buffer of data. */ static size_t lzx_compress(const void *restrict in, size_t in_nbytes, void *restrict out, size_t out_nbytes_avail, void *restrict _c) { struct lzx_compressor *c = _c; struct lzx_output_bitstream os; size_t result; /* Don't bother trying to compress very small inputs. */ if (in_nbytes < 64) return 0; /* If the compressor is in "destructive" mode, then we can directly * preprocess the input data. Otherwise, we need to copy it into an * internal buffer first. */ if (!c->destructive) { memcpy(c->in_buffer, in, in_nbytes); in = c->in_buffer; } /* Preprocess the input data. */ lzx_preprocess((void *)in, in_nbytes); /* Initially, the previous Huffman codeword lengths are all zeroes. */ c->codes_index = 0; memset(&c->codes[1].lens, 0, sizeof(struct lzx_lens)); /* Initialize the output bitstream. */ lzx_init_output(&os, out, out_nbytes_avail); /* Call the compression level-specific compress() function. */ (*c->impl)(c, in, in_nbytes, &os); /* Flush the output bitstream. */ result = lzx_flush_output(&os); /* If the data did not compress to less than its original size and we * preprocessed the original buffer, then postprocess it to restore it * to its original state. */ if (result == 0 && c->destructive) lzx_postprocess((void *)in, in_nbytes); /* Return the number of compressed bytes, or 0 if the input did not * compress to less than its original size. */ return result; } /* Free an LZX compressor. */ static void lzx_free_compressor(void *_c) { struct lzx_compressor *c = _c; if (!c->destructive) FREE(c->in_buffer); FREE(c); } const struct compressor_ops lzx_compressor_ops = { .get_needed_memory = lzx_get_needed_memory, .create_compressor = lzx_create_compressor, .compress = lzx_compress, .free_compressor = lzx_free_compressor, }; wimlib-1.13.1/src/blob_table.c0000644000175000017500000011560613160354224013032 00000000000000/* * blob_table.c * * A blob table maps SHA-1 message digests to "blobs", which are nonempty * sequences of binary data. Within a WIM file, blobs are single-instanced. * * This file also contains code to read and write the corresponding on-disk * representation of this table in the WIM file format. */ /* * Copyright (C) 2012-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include /* for unlink() */ #include "wimlib/assert.h" #include "wimlib/bitops.h" #include "wimlib/blob_table.h" #include "wimlib/encoding.h" #include "wimlib/endianness.h" #include "wimlib/error.h" #include "wimlib/metadata.h" #include "wimlib/ntfs_3g.h" #include "wimlib/resource.h" #include "wimlib/unaligned.h" #include "wimlib/util.h" #include "wimlib/win32.h" #include "wimlib/write.h" /* A hash table mapping SHA-1 message digests to blob descriptors */ struct blob_table { struct hlist_head *array; size_t num_blobs; size_t mask; /* capacity - 1; capacity is a power of 2 */ }; struct blob_table * new_blob_table(size_t capacity) { struct blob_table *table; struct hlist_head *array; capacity = roundup_pow_of_2(capacity); table = MALLOC(sizeof(struct blob_table)); if (table == NULL) goto oom; array = CALLOC(capacity, sizeof(array[0])); if (array == NULL) { FREE(table); goto oom; } table->num_blobs = 0; table->mask = capacity - 1; table->array = array; return table; oom: ERROR("Failed to allocate memory for blob table " "with capacity %zu", capacity); return NULL; } static int do_free_blob_descriptor(struct blob_descriptor *blob, void *_ignore) { free_blob_descriptor(blob); return 0; } void free_blob_table(struct blob_table *table) { if (table) { for_blob_in_table(table, do_free_blob_descriptor, NULL); FREE(table->array); FREE(table); } } struct blob_descriptor * new_blob_descriptor(void) { STATIC_ASSERT(BLOB_NONEXISTENT == 0); return CALLOC(1, sizeof(struct blob_descriptor)); } struct blob_descriptor * clone_blob_descriptor(const struct blob_descriptor *old) { struct blob_descriptor *new; new = memdup(old, sizeof(struct blob_descriptor)); if (new == NULL) return NULL; switch (new->blob_location) { case BLOB_IN_WIM: list_add(&new->rdesc_node, &new->rdesc->blob_list); break; case BLOB_IN_FILE_ON_DISK: #ifdef WITH_FUSE case BLOB_IN_STAGING_FILE: STATIC_ASSERT((void*)&old->file_on_disk == (void*)&old->staging_file_name); #endif new->file_on_disk = TSTRDUP(old->file_on_disk); if (new->file_on_disk == NULL) goto out_free; break; #ifdef __WIN32__ case BLOB_IN_WINDOWS_FILE: new->windows_file = clone_windows_file(old->windows_file); break; #endif case BLOB_IN_ATTACHED_BUFFER: new->attached_buffer = memdup(old->attached_buffer, old->size); if (new->attached_buffer == NULL) goto out_free; break; #ifdef WITH_NTFS_3G case BLOB_IN_NTFS_VOLUME: new->ntfs_loc = clone_ntfs_location(old->ntfs_loc); if (!new->ntfs_loc) goto out_free; break; #endif } return new; out_free: free_blob_descriptor(new); return NULL; } /* Release a blob descriptor from its location, if any, and set its new location * to BLOB_NONEXISTENT. */ void blob_release_location(struct blob_descriptor *blob) { switch (blob->blob_location) { case BLOB_IN_WIM: { struct wim_resource_descriptor *rdesc = blob->rdesc; list_del(&blob->rdesc_node); if (list_empty(&rdesc->blob_list)) { wim_decrement_refcnt(rdesc->wim); FREE(rdesc); } break; } case BLOB_IN_FILE_ON_DISK: #ifdef WITH_FUSE case BLOB_IN_STAGING_FILE: STATIC_ASSERT((void*)&blob->file_on_disk == (void*)&blob->staging_file_name); #endif case BLOB_IN_ATTACHED_BUFFER: STATIC_ASSERT((void*)&blob->file_on_disk == (void*)&blob->attached_buffer); FREE(blob->file_on_disk); break; #ifdef __WIN32__ case BLOB_IN_WINDOWS_FILE: free_windows_file(blob->windows_file); break; #endif #ifdef WITH_NTFS_3G case BLOB_IN_NTFS_VOLUME: free_ntfs_location(blob->ntfs_loc); break; #endif } blob->blob_location = BLOB_NONEXISTENT; } void free_blob_descriptor(struct blob_descriptor *blob) { if (blob) { blob_release_location(blob); FREE(blob); } } /* Should this blob be retained even if it has no references? */ static bool should_retain_blob(const struct blob_descriptor *blob) { return blob->blob_location == BLOB_IN_WIM; } static void finalize_blob(struct blob_descriptor *blob) { if (!should_retain_blob(blob)) free_blob_descriptor(blob); } /* * Decrements the reference count of the specified blob, which must be either * (a) unhashed, or (b) inserted in the specified blob table. * * If the blob's reference count reaches 0, we may unlink it from @table and * free it. However, we retain blobs with 0 reference count that originated * from WIM files (BLOB_IN_WIM). We do this for two reasons: * * 1. This prevents information about valid blobs in a WIM file --- blobs which * will continue to be present after appending to the WIM file --- from being * lost merely because we dropped all references to them. * * 2. Blob reference counts we read from WIM files can't be trusted. It's * possible that a WIM has reference counts that are too low; WIMGAPI * sometimes creates WIMs where this is the case. It's also possible that * blobs have been referenced from an external WIM; those blobs can * potentially have any reference count at all, either lower or higher than * would be expected for this WIM ("this WIM" meaning the owner of @table) if * it were a standalone WIM. * * So we can't take the reference counts too seriously. But at least, we do * recalculate by default when writing a new WIM file. */ void blob_decrement_refcnt(struct blob_descriptor *blob, struct blob_table *table) { blob_subtract_refcnt(blob, table, 1); } void blob_subtract_refcnt(struct blob_descriptor *blob, struct blob_table *table, u32 count) { if (unlikely(blob->refcnt < count)) { blob->refcnt = 0; /* See comment above */ return; } blob->refcnt -= count; if (blob->refcnt != 0) return; if (blob->unhashed) { list_del(&blob->unhashed_list); #ifdef WITH_FUSE /* If the blob has been extracted to a staging file for a FUSE * mount, unlink the staging file. (Note that there still may * be open file descriptors to it.) */ if (blob->blob_location == BLOB_IN_STAGING_FILE) unlinkat(blob->staging_dir_fd, blob->staging_file_name, 0); #endif } else { if (!should_retain_blob(blob)) blob_table_unlink(table, blob); } /* If FUSE mounts are enabled, then don't actually free the blob * descriptor until the last file descriptor to it has been closed. */ #ifdef WITH_FUSE if (blob->num_opened_fds == 0) #endif finalize_blob(blob); } #ifdef WITH_FUSE void blob_decrement_num_opened_fds(struct blob_descriptor *blob) { wimlib_assert(blob->num_opened_fds != 0); if (--blob->num_opened_fds == 0 && blob->refcnt == 0) finalize_blob(blob); } #endif static void blob_table_insert_raw(struct blob_table *table, struct blob_descriptor *blob) { size_t i = blob->hash_short & table->mask; hlist_add_head(&blob->hash_list, &table->array[i]); } static void enlarge_blob_table(struct blob_table *table) { size_t old_capacity, new_capacity; struct hlist_head *old_array, *new_array; struct blob_descriptor *blob; struct hlist_node *tmp; size_t i; old_capacity = table->mask + 1; new_capacity = old_capacity * 2; new_array = CALLOC(new_capacity, sizeof(struct hlist_head)); if (new_array == NULL) return; old_array = table->array; table->array = new_array; table->mask = new_capacity - 1; for (i = 0; i < old_capacity; i++) hlist_for_each_entry_safe(blob, tmp, &old_array[i], hash_list) blob_table_insert_raw(table, blob); FREE(old_array); } /* Insert a blob descriptor into the blob table. */ void blob_table_insert(struct blob_table *table, struct blob_descriptor *blob) { blob_table_insert_raw(table, blob); if (table->num_blobs++ > table->mask) enlarge_blob_table(table); } /* Unlinks a blob descriptor from the blob table; does not free it. */ void blob_table_unlink(struct blob_table *table, struct blob_descriptor *blob) { wimlib_assert(!blob->unhashed); wimlib_assert(table->num_blobs != 0); hlist_del(&blob->hash_list); table->num_blobs--; } /* Given a SHA-1 message digest, return the corresponding blob descriptor from * the specified blob table, or NULL if there is none. */ struct blob_descriptor * lookup_blob(const struct blob_table *table, const u8 *hash) { size_t i; struct blob_descriptor *blob; i = load_size_t_unaligned(hash) & table->mask; hlist_for_each_entry(blob, &table->array[i], hash_list) if (hashes_equal(hash, blob->hash)) return blob; return NULL; } /* Call a function on all blob descriptors in the specified blob table. Stop * early and return nonzero if any call to the function returns nonzero. */ int for_blob_in_table(struct blob_table *table, int (*visitor)(struct blob_descriptor *, void *), void *arg) { struct blob_descriptor *blob; struct hlist_node *tmp; int ret; for (size_t i = 0; i <= table->mask; i++) { hlist_for_each_entry_safe(blob, tmp, &table->array[i], hash_list) { ret = visitor(blob, arg); if (ret) return ret; } } return 0; } /* * This is a qsort() callback that sorts blobs into an order optimized for * reading. Sorting is done primarily by blob location, then secondarily by a * location-dependent order. For example, blobs in WIM resources are sorted * such that the underlying WIM files will be read sequentially. This is * especially important for WIM files containing solid resources. */ int cmp_blobs_by_sequential_order(const void *p1, const void *p2) { const struct blob_descriptor *blob1, *blob2; int v; WIMStruct *wim1, *wim2; blob1 = *(const struct blob_descriptor**)p1; blob2 = *(const struct blob_descriptor**)p2; v = (int)blob1->blob_location - (int)blob2->blob_location; /* Different locations? Note: "unsafe compaction mode" requires that * blobs in WIMs sort before all others. For the logic here to ensure * this, BLOB_IN_WIM must have the lowest value among all defined * blob_locations. Statically verify that the enum values haven't * changed. */ STATIC_ASSERT(BLOB_NONEXISTENT == 0 && BLOB_IN_WIM == 1); if (v) return v; switch (blob1->blob_location) { case BLOB_IN_WIM: wim1 = blob1->rdesc->wim; wim2 = blob2->rdesc->wim; /* Different WIM files? */ if (wim1 != wim2) { /* Resources from the WIM file currently being compacted * (if any) must always sort first. */ v = (int)wim2->being_compacted - (int)wim1->being_compacted; if (v) return v; /* Different split WIMs? */ v = cmp_guids(wim1->hdr.guid, wim2->hdr.guid); if (v) return v; /* Different part numbers in the same split WIM? */ v = (int)wim1->hdr.part_number - (int)wim2->hdr.part_number; if (v) return v; /* Probably two WIMStructs for the same on-disk file. * Just sort by pointer. */ return wim1 < wim2 ? -1 : 1; } /* Same WIM file */ /* Sort by increasing resource offset */ if (blob1->rdesc->offset_in_wim != blob2->rdesc->offset_in_wim) return cmp_u64(blob1->rdesc->offset_in_wim, blob2->rdesc->offset_in_wim); /* The blobs are in the same solid resource. Sort by increasing * offset in the resource. */ return cmp_u64(blob1->offset_in_res, blob2->offset_in_res); case BLOB_IN_FILE_ON_DISK: #ifdef WITH_FUSE case BLOB_IN_STAGING_FILE: #endif /* Compare files by path: just a heuristic that will place files * in the same directory next to each other. */ return tstrcmp(blob1->file_on_disk, blob2->file_on_disk); #ifdef __WIN32__ case BLOB_IN_WINDOWS_FILE: return cmp_windows_files(blob1->windows_file, blob2->windows_file); #endif #ifdef WITH_NTFS_3G case BLOB_IN_NTFS_VOLUME: return cmp_ntfs_locations(blob1->ntfs_loc, blob2->ntfs_loc); #endif default: /* No additional sorting order defined for this resource * location (e.g. BLOB_IN_ATTACHED_BUFFER); simply compare * everything equal to each other. */ return 0; } } int sort_blob_list(struct list_head *blob_list, size_t list_head_offset, int (*compar)(const void *, const void*)) { struct list_head *cur; struct blob_descriptor **array; size_t i; size_t array_size; size_t num_blobs = 0; list_for_each(cur, blob_list) num_blobs++; if (num_blobs <= 1) return 0; array_size = num_blobs * sizeof(array[0]); array = MALLOC(array_size); if (array == NULL) return WIMLIB_ERR_NOMEM; cur = blob_list->next; for (i = 0; i < num_blobs; i++) { array[i] = (struct blob_descriptor*)((u8*)cur - list_head_offset); cur = cur->next; } qsort(array, num_blobs, sizeof(array[0]), compar); INIT_LIST_HEAD(blob_list); for (i = 0; i < num_blobs; i++) { list_add_tail((struct list_head*) ((u8*)array[i] + list_head_offset), blob_list); } FREE(array); return 0; } /* Sort the specified list of blobs in an order optimized for sequential * reading. */ int sort_blob_list_by_sequential_order(struct list_head *blob_list, size_t list_head_offset) { return sort_blob_list(blob_list, list_head_offset, cmp_blobs_by_sequential_order); } static int add_blob_to_array(struct blob_descriptor *blob, void *_pp) { struct blob_descriptor ***pp = _pp; *(*pp)++ = blob; return 0; } /* Iterate through the blob descriptors in the specified blob table in an order * optimized for sequential reading. */ int for_blob_in_table_sorted_by_sequential_order(struct blob_table *table, int (*visitor)(struct blob_descriptor *, void *), void *arg) { struct blob_descriptor **blob_array, **p; size_t num_blobs = table->num_blobs; int ret; blob_array = MALLOC(num_blobs * sizeof(blob_array[0])); if (!blob_array) return WIMLIB_ERR_NOMEM; p = blob_array; for_blob_in_table(table, add_blob_to_array, &p); wimlib_assert(p == blob_array + num_blobs); qsort(blob_array, num_blobs, sizeof(blob_array[0]), cmp_blobs_by_sequential_order); ret = 0; for (size_t i = 0; i < num_blobs; i++) { ret = visitor(blob_array[i], arg); if (ret) break; } FREE(blob_array); return ret; } /* On-disk format of a blob descriptor in a WIM file. * * Note: if the WIM file contains solid resource(s), then this structure is * sometimes overloaded to describe a "resource" rather than a "blob". See the * code for details. */ struct blob_descriptor_disk { /* Size, offset, and flags of the blob. */ struct wim_reshdr_disk reshdr; /* Which part of the split WIM this blob is in; indexed from 1. */ le16 part_number; /* Reference count of this blob over all WIM images. (But see comment * above blob_decrement_refcnt().) */ le32 refcnt; /* SHA-1 message digest of the uncompressed data of this blob, or all * zeroes if this blob is of zero length. */ u8 hash[SHA1_HASH_SIZE]; } _packed_attribute; /* Given a nonempty run of consecutive blob descriptors with the SOLID flag set, * count how many specify resources (as opposed to blobs within those * resources). * * Returns the resulting count. */ static size_t count_solid_resources(const struct blob_descriptor_disk *entries, size_t max) { size_t count = 0; do { struct wim_reshdr reshdr; get_wim_reshdr(&(entries++)->reshdr, &reshdr); if (!(reshdr.flags & WIM_RESHDR_FLAG_SOLID)) { /* Run was terminated by a stand-alone blob entry. */ break; } if (reshdr.uncompressed_size == SOLID_RESOURCE_MAGIC_NUMBER) { /* This is a resource entry. */ count++; } } while (--max); return count; } /* * Given a run of consecutive blob descriptors with the SOLID flag set and * having @num_rdescs resource entries, load resource information from them into * the resource descriptors in the @rdescs array. * * Returns 0 on success, or a nonzero error code on failure. */ static int do_load_solid_info(WIMStruct *wim, struct wim_resource_descriptor **rdescs, size_t num_rdescs, const struct blob_descriptor_disk *entries) { for (size_t i = 0; i < num_rdescs; i++) { struct wim_reshdr reshdr; struct alt_chunk_table_header_disk hdr; struct wim_resource_descriptor *rdesc; int ret; /* Advance to next resource entry. */ do { get_wim_reshdr(&(entries++)->reshdr, &reshdr); } while (reshdr.uncompressed_size != SOLID_RESOURCE_MAGIC_NUMBER); rdesc = rdescs[i]; wim_reshdr_to_desc(&reshdr, wim, rdesc); /* For solid resources, the uncompressed size, compression type, * and chunk size are stored in the resource itself, not in the * blob table. */ ret = full_pread(&wim->in_fd, &hdr, sizeof(hdr), reshdr.offset_in_wim); if (ret) { ERROR("Failed to read header of solid resource " "(offset_in_wim=%"PRIu64")", reshdr.offset_in_wim); return ret; } rdesc->uncompressed_size = le64_to_cpu(hdr.res_usize); /* Compression format numbers must be the same as in * WIMGAPI to be compatible here. */ STATIC_ASSERT(WIMLIB_COMPRESSION_TYPE_NONE == 0); STATIC_ASSERT(WIMLIB_COMPRESSION_TYPE_XPRESS == 1); STATIC_ASSERT(WIMLIB_COMPRESSION_TYPE_LZX == 2); STATIC_ASSERT(WIMLIB_COMPRESSION_TYPE_LZMS == 3); rdesc->compression_type = le32_to_cpu(hdr.compression_format); rdesc->chunk_size = le32_to_cpu(hdr.chunk_size); } return 0; } /* * Given a nonempty run of consecutive blob descriptors with the SOLID flag set, * allocate a 'struct wim_resource_descriptor' for each resource within that * run. * * Returns 0 on success, or a nonzero error code on failure. * Returns the pointers and count in *rdescs_ret and *num_rdescs_ret. */ static int load_solid_info(WIMStruct *wim, const struct blob_descriptor_disk *entries, size_t num_remaining_entries, struct wim_resource_descriptor ***rdescs_ret, size_t *num_rdescs_ret) { size_t num_rdescs; struct wim_resource_descriptor **rdescs; size_t i; int ret; num_rdescs = count_solid_resources(entries, num_remaining_entries); rdescs = CALLOC(num_rdescs, sizeof(rdescs[0])); if (!rdescs) return WIMLIB_ERR_NOMEM; for (i = 0; i < num_rdescs; i++) { rdescs[i] = MALLOC(sizeof(struct wim_resource_descriptor)); if (!rdescs[i]) { ret = WIMLIB_ERR_NOMEM; goto out_free_rdescs; } } ret = do_load_solid_info(wim, rdescs, num_rdescs, entries); if (ret) goto out_free_rdescs; wim->refcnt += num_rdescs; *rdescs_ret = rdescs; *num_rdescs_ret = num_rdescs; return 0; out_free_rdescs: for (i = 0; i < num_rdescs; i++) FREE(rdescs[i]); FREE(rdescs); return ret; } /* Given a 'struct blob_descriptor' allocated for an on-disk blob descriptor * with the SOLID flag set, try to assign it to resource in the current solid * run. */ static int assign_blob_to_solid_resource(const struct wim_reshdr *reshdr, struct blob_descriptor *blob, struct wim_resource_descriptor **rdescs, size_t num_rdescs) { u64 offset = reshdr->offset_in_wim; /* XXX: This linear search will be slow in the degenerate case where the * number of solid resources in the run is huge. */ blob->size = reshdr->size_in_wim; for (size_t i = 0; i < num_rdescs; i++) { if (offset + blob->size <= rdescs[i]->uncompressed_size) { blob_set_is_located_in_wim_resource(blob, rdescs[i], offset); return 0; } offset -= rdescs[i]->uncompressed_size; } ERROR("blob could not be assigned to a solid resource"); return WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY; } static void free_solid_rdescs(struct wim_resource_descriptor **rdescs, size_t num_rdescs) { if (rdescs) { for (size_t i = 0; i < num_rdescs; i++) { if (list_empty(&rdescs[i]->blob_list)) { rdescs[i]->wim->refcnt--; FREE(rdescs[i]); } } FREE(rdescs); } } static int cmp_blobs_by_offset_in_res(const void *p1, const void *p2) { const struct blob_descriptor *blob1, *blob2; blob1 = *(const struct blob_descriptor**)p1; blob2 = *(const struct blob_descriptor**)p2; return cmp_u64(blob1->offset_in_res, blob2->offset_in_res); } /* Validate the size and location of a WIM resource. */ static int validate_resource(struct wim_resource_descriptor *rdesc) { struct blob_descriptor *blob; bool out_of_order; u64 expected_next_offset; int ret; /* Verify that the resource itself has a valid offset and size. */ if (rdesc->offset_in_wim + rdesc->size_in_wim < rdesc->size_in_wim) goto invalid_due_to_overflow; /* Verify that each blob in the resource has a valid offset and size. */ expected_next_offset = 0; out_of_order = false; list_for_each_entry(blob, &rdesc->blob_list, rdesc_node) { if (blob->offset_in_res + blob->size < blob->size || blob->offset_in_res + blob->size > rdesc->uncompressed_size) goto invalid_due_to_overflow; if (blob->offset_in_res >= expected_next_offset) expected_next_offset = blob->offset_in_res + blob->size; else out_of_order = true; } /* If the blobs were not located at strictly increasing positions (not * allowing for overlap), sort them. Then make sure that none overlap. */ if (out_of_order) { ret = sort_blob_list(&rdesc->blob_list, offsetof(struct blob_descriptor, rdesc_node), cmp_blobs_by_offset_in_res); if (ret) return ret; expected_next_offset = 0; list_for_each_entry(blob, &rdesc->blob_list, rdesc_node) { if (blob->offset_in_res >= expected_next_offset) expected_next_offset = blob->offset_in_res + blob->size; else goto invalid_due_to_overlap; } } return 0; invalid_due_to_overflow: ERROR("Invalid blob table (offset overflow)"); return WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY; invalid_due_to_overlap: ERROR("Invalid blob table (blobs in solid resource overlap)"); return WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY; } static int finish_solid_rdescs(struct wim_resource_descriptor **rdescs, size_t num_rdescs) { int ret = 0; for (size_t i = 0; i < num_rdescs; i++) { ret = validate_resource(rdescs[i]); if (ret) break; } free_solid_rdescs(rdescs, num_rdescs); return ret; } /* * read_blob_table() - * * Read the blob table from a WIM file. Usually, each entry in this table * describes a "blob", or equivalently a "resource", that the WIM file contains, * along with its location and SHA-1 message digest. Descriptors for * non-metadata blobs will be saved in the in-memory blob table * (wim->blob_table), whereas descriptors for metadata blobs will be saved in a * special location per-image (the wim->image_metadata array). * * However, in WIM_VERSION_SOLID (3584) WIMs, a resource may contain multiple * blobs that are compressed together. Such a resource is called a "solid * resource". Solid resources are still described in the on-disk "blob table", * although the format is not the most logical. A consecutive sequence of * entries that all have flag WIM_RESHDR_FLAG_SOLID (0x10) set is a "solid run". * A solid run describes a set of solid resources, each of which contains a set * of blobs. In a solid run, a 'struct wim_reshdr_disk' with 'uncompressed_size * = SOLID_RESOURCE_MAGIC_NUMBER (0x100000000)' specifies a solid resource, * whereas any other 'struct wim_reshdr_disk' specifies a blob within a solid * resource. There are some oddities in how we need to determine which solid * resource a blob is actually in; see the code for details. * * Possible return values: * WIMLIB_ERR_SUCCESS (0) * WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY * WIMLIB_ERR_NOMEM * * Or an error code caused by failure to read the blob table from the WIM * file. */ int read_blob_table(WIMStruct *wim) { int ret; size_t num_entries; void *buf = NULL; struct blob_table *table = NULL; struct blob_descriptor *cur_blob = NULL; size_t num_duplicate_blobs = 0; size_t num_empty_blobs = 0; size_t num_wrong_part_blobs = 0; u32 image_index = 0; struct wim_resource_descriptor **cur_solid_rdescs = NULL; size_t cur_num_solid_rdescs = 0; /* Calculate the number of entries in the blob table. */ num_entries = wim->hdr.blob_table_reshdr.uncompressed_size / sizeof(struct blob_descriptor_disk); /* Read the blob table into a buffer. */ ret = wim_reshdr_to_data(&wim->hdr.blob_table_reshdr, wim, &buf); if (ret) goto out; /* Allocate a hash table to map SHA-1 message digests into blob * descriptors. This is the in-memory "blob table". */ table = new_blob_table(num_entries); if (!table) goto oom; /* Allocate and initalize blob descriptors from the raw blob table * buffer. */ for (size_t i = 0; i < num_entries; i++) { const struct blob_descriptor_disk *disk_entry = &((const struct blob_descriptor_disk*)buf)[i]; struct wim_reshdr reshdr; u16 part_number; /* Get the resource header */ get_wim_reshdr(&disk_entry->reshdr, &reshdr); /* Ignore SOLID flag if it isn't supposed to be used in this WIM * version. */ if (wim->hdr.wim_version == WIM_VERSION_DEFAULT) reshdr.flags &= ~WIM_RESHDR_FLAG_SOLID; /* Allocate a new 'struct blob_descriptor'. */ cur_blob = new_blob_descriptor(); if (!cur_blob) goto oom; /* Get the part number, reference count, and hash. */ part_number = le16_to_cpu(disk_entry->part_number); cur_blob->refcnt = le32_to_cpu(disk_entry->refcnt); copy_hash(cur_blob->hash, disk_entry->hash); if (reshdr.flags & WIM_RESHDR_FLAG_SOLID) { /* SOLID entry */ if (!cur_solid_rdescs) { /* Starting new run */ ret = load_solid_info(wim, disk_entry, num_entries - i, &cur_solid_rdescs, &cur_num_solid_rdescs); if (ret) goto out; } if (reshdr.uncompressed_size == SOLID_RESOURCE_MAGIC_NUMBER) { /* Resource entry, not blob entry */ goto free_cur_blob_and_continue; } /* Blob entry */ ret = assign_blob_to_solid_resource(&reshdr, cur_blob, cur_solid_rdescs, cur_num_solid_rdescs); if (ret) goto out; } else { /* Normal blob/resource entry; SOLID not set. */ struct wim_resource_descriptor *rdesc; if (unlikely(cur_solid_rdescs)) { /* This entry terminated a solid run. */ ret = finish_solid_rdescs(cur_solid_rdescs, cur_num_solid_rdescs); cur_solid_rdescs = NULL; if (ret) goto out; } if (unlikely(!(reshdr.flags & WIM_RESHDR_FLAG_COMPRESSED) && (reshdr.size_in_wim != reshdr.uncompressed_size))) { ERROR("Uncompressed resource has " "size_in_wim != uncompressed_size"); ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY; goto out; } /* Set up a resource descriptor for this blob. */ rdesc = MALLOC(sizeof(struct wim_resource_descriptor)); if (!rdesc) goto oom; wim_reshdr_to_desc_and_blob(&reshdr, wim, rdesc, cur_blob); wim->refcnt++; } /* cur_blob is now a blob bound to a resource. */ /* Ignore entries with all zeroes in the hash field. */ if (unlikely(is_zero_hash(cur_blob->hash))) goto free_cur_blob_and_continue; /* Verify that the blob has nonzero size. */ if (unlikely(cur_blob->size == 0)) { num_empty_blobs++; goto free_cur_blob_and_continue; } /* Verify that the part number matches that of the underlying * WIM file. */ if (unlikely(part_number != wim->hdr.part_number)) { num_wrong_part_blobs++; goto free_cur_blob_and_continue; } if (reshdr.flags & WIM_RESHDR_FLAG_METADATA) { /* Blob table entry for a metadata resource. */ /* Metadata entries with no references must be ignored. * See, for example, the WinPE WIMs from the WAIK v2.1. */ if (cur_blob->refcnt == 0) goto free_cur_blob_and_continue; if (cur_blob->refcnt != 1) { /* We don't currently support this case due to * the complications of multiple images sharing * the same metadata resource or a metadata * resource also being referenced by files. */ ERROR("Found metadata resource with refcnt != 1"); ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY; goto out; } if (reshdr.flags & WIM_RESHDR_FLAG_SOLID) { ERROR("Image metadata in solid resources " "is unsupported."); ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY; goto out; } if (wim->hdr.part_number != 1) { WARNING("Ignoring metadata resource found in a " "non-first part of the split WIM"); goto free_cur_blob_and_continue; } /* The number of entries in the blob table with * WIM_RESHDR_FLAG_METADATA set should be the same as * the image_count field in the WIM header. */ if (image_index == wim->hdr.image_count) { WARNING("Found more metadata resources than images"); goto free_cur_blob_and_continue; } /* Notice very carefully: We are assigning the metadata * resources to images in the same order in which their * blob table entries occur on disk. (This is also the * behavior of Microsoft's software.) In particular, * this overrides the actual locations of the metadata * resources themselves in the WIM file as well as any * information written in the XML data. */ wim->image_metadata[image_index] = new_unloaded_image_metadata(cur_blob); if (!wim->image_metadata[image_index]) goto oom; image_index++; } else { /* Blob table entry for a non-metadata blob. */ /* Ignore this blob if it's a duplicate. */ if (lookup_blob(table, cur_blob->hash)) { num_duplicate_blobs++; goto free_cur_blob_and_continue; } /* Insert the blob into the in-memory blob table, keyed * by its SHA-1 message digest. */ blob_table_insert(table, cur_blob); } continue; free_cur_blob_and_continue: if (cur_solid_rdescs && cur_blob->blob_location == BLOB_IN_WIM) blob_unset_is_located_in_wim_resource(cur_blob); free_blob_descriptor(cur_blob); } cur_blob = NULL; if (cur_solid_rdescs) { /* End of blob table terminated a solid run. */ ret = finish_solid_rdescs(cur_solid_rdescs, cur_num_solid_rdescs); cur_solid_rdescs = NULL; if (ret) goto out; } if (wim->hdr.part_number == 1 && image_index != wim->hdr.image_count) { WARNING("Could not find metadata resources for all images"); wim->hdr.image_count = image_index; } if (num_duplicate_blobs > 0) WARNING("Ignoring %zu duplicate blobs", num_duplicate_blobs); if (num_empty_blobs > 0) WARNING("Ignoring %zu empty blobs", num_empty_blobs); if (num_wrong_part_blobs > 0) { WARNING("Ignoring %zu blobs with wrong part number", num_wrong_part_blobs); } wim->blob_table = table; ret = 0; goto out_free_buf; oom: ERROR("Not enough memory to read blob table!"); ret = WIMLIB_ERR_NOMEM; out: free_solid_rdescs(cur_solid_rdescs, cur_num_solid_rdescs); free_blob_descriptor(cur_blob); free_blob_table(table); out_free_buf: FREE(buf); return ret; } static void write_blob_descriptor(struct blob_descriptor_disk *disk_entry, const struct wim_reshdr *out_reshdr, u16 part_number, u32 refcnt, const u8 *hash) { put_wim_reshdr(out_reshdr, &disk_entry->reshdr); disk_entry->part_number = cpu_to_le16(part_number); disk_entry->refcnt = cpu_to_le32(refcnt); copy_hash(disk_entry->hash, hash); } /* Note: the list of blob descriptors must be sorted so that all entries for the * same solid resource are consecutive. In addition, blob descriptors for * metadata resources must be in the same order as the indices of the underlying * images. */ int write_blob_table_from_blob_list(struct list_head *blob_list, struct filedes *out_fd, u16 part_number, struct wim_reshdr *out_reshdr, int write_resource_flags) { size_t table_size; struct blob_descriptor *blob; struct blob_descriptor_disk *table_buf; struct blob_descriptor_disk *table_buf_ptr; int ret; u64 prev_res_offset_in_wim = ~0ULL; u64 prev_uncompressed_size; u64 logical_offset; table_size = 0; list_for_each_entry(blob, blob_list, blob_table_list) { table_size += sizeof(struct blob_descriptor_disk); if (blob->out_reshdr.flags & WIM_RESHDR_FLAG_SOLID && blob->out_res_offset_in_wim != prev_res_offset_in_wim) { table_size += sizeof(struct blob_descriptor_disk); prev_res_offset_in_wim = blob->out_res_offset_in_wim; } } table_buf = MALLOC(table_size); if (table_buf == NULL) { ERROR("Failed to allocate %zu bytes for temporary blob table", table_size); return WIMLIB_ERR_NOMEM; } table_buf_ptr = table_buf; prev_res_offset_in_wim = ~0ULL; prev_uncompressed_size = 0; logical_offset = 0; list_for_each_entry(blob, blob_list, blob_table_list) { if (blob->out_reshdr.flags & WIM_RESHDR_FLAG_SOLID) { struct wim_reshdr tmp_reshdr; /* Eww. When WIMGAPI sees multiple solid resources, it * expects the offsets to be adjusted as if there were * really only one solid resource. */ if (blob->out_res_offset_in_wim != prev_res_offset_in_wim) { /* Put the resource entry for solid resource */ tmp_reshdr.offset_in_wim = blob->out_res_offset_in_wim; tmp_reshdr.size_in_wim = blob->out_res_size_in_wim; tmp_reshdr.uncompressed_size = SOLID_RESOURCE_MAGIC_NUMBER; tmp_reshdr.flags = WIM_RESHDR_FLAG_SOLID; write_blob_descriptor(table_buf_ptr++, &tmp_reshdr, part_number, 1, zero_hash); logical_offset += prev_uncompressed_size; prev_res_offset_in_wim = blob->out_res_offset_in_wim; prev_uncompressed_size = blob->out_res_uncompressed_size; } tmp_reshdr = blob->out_reshdr; tmp_reshdr.offset_in_wim += logical_offset; write_blob_descriptor(table_buf_ptr++, &tmp_reshdr, part_number, blob->out_refcnt, blob->hash); } else { write_blob_descriptor(table_buf_ptr++, &blob->out_reshdr, part_number, blob->out_refcnt, blob->hash); } } wimlib_assert((u8*)table_buf_ptr - (u8*)table_buf == table_size); /* Write the blob table uncompressed. Although wimlib can handle a * compressed blob table, MS software cannot. */ ret = write_wim_resource_from_buffer(table_buf, table_size, true, out_fd, WIMLIB_COMPRESSION_TYPE_NONE, 0, out_reshdr, NULL, write_resource_flags); FREE(table_buf); return ret; } /* Allocate a blob descriptor for the contents of the buffer, or re-use an * existing descriptor in @blob_table for an identical blob. */ struct blob_descriptor * new_blob_from_data_buffer(const void *buffer, size_t size, struct blob_table *blob_table) { u8 hash[SHA1_HASH_SIZE]; struct blob_descriptor *blob; void *buffer_copy; sha1_buffer(buffer, size, hash); blob = lookup_blob(blob_table, hash); if (blob) return blob; blob = new_blob_descriptor(); if (!blob) return NULL; buffer_copy = memdup(buffer, size); if (!buffer_copy) { free_blob_descriptor(blob); return NULL; } blob_set_is_located_in_attached_buffer(blob, buffer_copy, size); copy_hash(blob->hash, hash); blob_table_insert(blob_table, blob); return blob; } struct blob_descriptor * after_blob_hashed(struct blob_descriptor *blob, struct blob_descriptor **back_ptr, struct blob_table *blob_table) { struct blob_descriptor *duplicate_blob; list_del(&blob->unhashed_list); blob->unhashed = 0; /* Look for a duplicate blob */ duplicate_blob = lookup_blob(blob_table, blob->hash); if (duplicate_blob) { /* We have a duplicate blob. Transfer the reference counts from * this blob to the duplicate and update the reference to this * blob (from a stream) to point to the duplicate. The caller * is responsible for freeing @blob if needed. */ wimlib_assert(duplicate_blob->size == blob->size); duplicate_blob->refcnt += blob->refcnt; blob->refcnt = 0; *back_ptr = duplicate_blob; return duplicate_blob; } else { /* No duplicate blob, so we need to insert this blob into the * blob table and treat it as a hashed blob. */ blob_table_insert(blob_table, blob); return blob; } } /* * Calculate the SHA-1 message digest of a blob and move its descriptor from the * list of unhashed blobs to the blob table, possibly joining it with an * identical blob. * * @blob: * The blob to hash * @blob_table: * The blob table in which the blob needs to be indexed * @blob_ret: * On success, a pointer to the resulting blob descriptor is written to * this location. This will be the same as @blob if it was inserted into * the blob table, or different if a duplicate blob was found. * * Returns 0 on success; nonzero if there is an error reading the blob data. */ int hash_unhashed_blob(struct blob_descriptor *blob, struct blob_table *blob_table, struct blob_descriptor **blob_ret) { struct blob_descriptor **back_ptr; int ret; back_ptr = retrieve_pointer_to_unhashed_blob(blob); ret = sha1_blob(blob); if (ret) return ret; *blob_ret = after_blob_hashed(blob, back_ptr, blob_table); return 0; } void blob_to_wimlib_resource_entry(const struct blob_descriptor *blob, struct wimlib_resource_entry *wentry) { memset(wentry, 0, sizeof(*wentry)); wentry->uncompressed_size = blob->size; if (blob->blob_location == BLOB_IN_WIM) { unsigned res_flags = blob->rdesc->flags; wentry->part_number = blob->rdesc->wim->hdr.part_number; if (res_flags & WIM_RESHDR_FLAG_SOLID) { wentry->offset = blob->offset_in_res; } else { wentry->compressed_size = blob->rdesc->size_in_wim; wentry->offset = blob->rdesc->offset_in_wim; } wentry->raw_resource_offset_in_wim = blob->rdesc->offset_in_wim; wentry->raw_resource_compressed_size = blob->rdesc->size_in_wim; wentry->raw_resource_uncompressed_size = blob->rdesc->uncompressed_size; wentry->is_compressed = (res_flags & WIM_RESHDR_FLAG_COMPRESSED) != 0; wentry->is_free = (res_flags & WIM_RESHDR_FLAG_FREE) != 0; wentry->is_spanned = (res_flags & WIM_RESHDR_FLAG_SPANNED) != 0; wentry->packed = (res_flags & WIM_RESHDR_FLAG_SOLID) != 0; } if (!blob->unhashed) copy_hash(wentry->sha1_hash, blob->hash); wentry->reference_count = blob->refcnt; wentry->is_metadata = blob->is_metadata; } struct iterate_blob_context { wimlib_iterate_lookup_table_callback_t cb; void *user_ctx; }; static int do_iterate_blob(struct blob_descriptor *blob, void *_ctx) { struct iterate_blob_context *ctx = _ctx; struct wimlib_resource_entry entry; blob_to_wimlib_resource_entry(blob, &entry); return (*ctx->cb)(&entry, ctx->user_ctx); } /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_iterate_lookup_table(WIMStruct *wim, int flags, wimlib_iterate_lookup_table_callback_t cb, void *user_ctx) { if (flags != 0) return WIMLIB_ERR_INVALID_PARAM; struct iterate_blob_context ctx = { .cb = cb, .user_ctx = user_ctx, }; if (wim_has_metadata(wim)) { int ret; for (int i = 0; i < wim->hdr.image_count; i++) { struct blob_descriptor *blob; struct wim_image_metadata *imd = wim->image_metadata[i]; ret = do_iterate_blob(imd->metadata_blob, &ctx); if (ret) return ret; image_for_each_unhashed_blob(blob, imd) { ret = do_iterate_blob(blob, &ctx); if (ret) return ret; } } } return for_blob_in_table(wim->blob_table, do_iterate_blob, &ctx); } wimlib-1.13.1/src/pathlist.c0000644000175000017500000000254713324700265012576 00000000000000/* * pathlist.c * * Utility function for reading path list files. */ /* * Copyright (C) 2013 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib/pathlist.h" #include "wimlib/textfile.h" int read_path_list_file(const tchar *listfile, tchar ***paths_ret, size_t *num_paths_ret, void **mem_ret) { STRING_LIST(paths); struct text_file_section tmp = { .name = T(""), .strings = &paths, }; void *buf; int ret; ret = load_text_file(listfile, NULL, 0, &buf, &tmp, 1, LOAD_TEXT_FILE_REMOVE_QUOTES | LOAD_TEXT_FILE_ALLOW_STDIN, NULL); if (ret) return ret; *paths_ret = paths.strings; *num_paths_ret = paths.num_strings; *mem_ret = buf; return 0; } wimlib-1.13.1/src/tagged_items.c0000644000175000017500000001431413376063254013404 00000000000000/* * tagged_items.c * * Support for tagged metadata items that can be appended to WIM directory * entries. */ /* * Copyright (C) 2014-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib/assert.h" #include "wimlib/endianness.h" #include "wimlib/inode.h" #include "wimlib/tagged_items.h" #include "wimlib/unix_data.h" /* * Header that begins each tagged metadata item associated with a file in a WIM * metadata resource */ struct tagged_item_header { /* identifies the type of metadata item (see TAG_* constants) */ le32 tag; /* size of this item's data in bytes, excluding this header */ le32 length; /* followed by the item's data */ u8 data[0]; /* then zero-padded to an 8-byte boundary */ } _aligned_attribute(8); /* * Retrieve from @inode the first metadata item that is tagged with @tag and * contains at least @min_len bytes of data. If found, return a pointer to the * item's data and write its actual length to @actual_len_ret if not NULL. If * not found, return NULL. */ void * inode_get_tagged_item(const struct wim_inode *inode, u32 tag, u32 min_len, u32 *actual_len_ret) { struct tagged_item_header *hdr; size_t len_remaining; STATIC_ASSERT(sizeof(*hdr) == 8); if (!inode->i_extra) return NULL; hdr = (struct tagged_item_header *)inode->i_extra->data; len_remaining = inode->i_extra->size; /* Iterate through the tagged items. */ while (len_remaining >= sizeof(*hdr) + min_len) { u32 len = le32_to_cpu(hdr->length); u32 full_len = sizeof(*hdr) + ALIGN(len, 8); /* Length overflow (corrupted item list)? */ if (unlikely(full_len < len || full_len > len_remaining)) return NULL; /* Matches the item we wanted? */ if (le32_to_cpu(hdr->tag) == tag && len >= min_len) { if (actual_len_ret) *actual_len_ret = len; return hdr->data; } len_remaining -= full_len; hdr = (struct tagged_item_header *)((u8 *)hdr + full_len); } return NULL; } /* * Add a tagged item to the specified inode and return a pointer to its * uninitialized data, which the caller must initialize. No check is made for * whether the inode already has item(s) with the specified tag. */ static void * inode_add_tagged_item(struct wim_inode *inode, u32 tag, u32 len) { struct wim_inode_extra *extra; struct tagged_item_header *hdr; size_t oldsize = (inode->i_extra ? inode->i_extra->size : 0); size_t newsize = oldsize + sizeof(*hdr) + ALIGN(len, 8); wimlib_assert(oldsize % 8 == 0); extra = REALLOC(inode->i_extra, sizeof(*extra) + newsize); if (!extra) return NULL; inode->i_extra = extra; extra->size = newsize; hdr = (struct tagged_item_header *)&extra->data[oldsize]; hdr->tag = cpu_to_le32(tag); hdr->length = cpu_to_le32(len); memset(hdr->data + len, 0, -len & 7); /* pad to next 8-byte boundary */ return hdr->data; } /* * Add a tagged item containing the specified data to the specified inode, first * removing any existing items with the same tag. Returns %true if successful, * %false if failed (out of memory). */ bool inode_set_tagged_item(struct wim_inode *inode, u32 tag, const void *data, u32 len) { u8 *p; u32 old_len; /* Remove any existing items with the same tag */ while ((p = inode_get_tagged_item(inode, tag, 0, &old_len)) != NULL) { p -= sizeof(struct tagged_item_header); old_len += sizeof(struct tagged_item_header); old_len = ALIGN(old_len, 8); memmove(p, p + old_len, (inode->i_extra->data + inode->i_extra->size) - (p + old_len)); inode->i_extra->size -= old_len; } /* Add the new item */ p = inode_add_tagged_item(inode, tag, len); if (!p) return false; memcpy(p, data, len); return true; } struct wimlib_unix_data_disk { le32 uid; le32 gid; le32 mode; le32 rdev; }; static inline struct wimlib_unix_data_disk * inode_get_unix_data_disk(const struct wim_inode *inode) { return inode_get_tagged_item(inode, TAG_WIMLIB_UNIX_DATA, sizeof(struct wimlib_unix_data_disk), NULL); } /* Return %true iff the specified inode has standard UNIX metadata. */ bool inode_has_unix_data(const struct wim_inode *inode) { return inode_get_unix_data_disk(inode) != NULL; } /* * Get an inode's standard UNIX metadata. * * If the inode has standard UNIX metadata, returns %true and fills @unix_data. * Otherwise returns %false. */ bool inode_get_unix_data(const struct wim_inode *inode, struct wimlib_unix_data *unix_data) { const struct wimlib_unix_data_disk *p; p = inode_get_unix_data_disk(inode); if (!p) return false; unix_data->uid = le32_to_cpu(p->uid); unix_data->gid = le32_to_cpu(p->gid); unix_data->mode = le32_to_cpu(p->mode); unix_data->rdev = le32_to_cpu(p->rdev); return true; } /* * Set an inode's standard UNIX metadata. * * Callers must specify all members in @unix_data. If the inode does not yet * have standard UNIX metadata, it is given these values. Otherwise, only the * values that also have the corresponding flags in @which set are changed. * * Returns %true if successful, %false if failed (out of memory). */ bool inode_set_unix_data(struct wim_inode *inode, struct wimlib_unix_data *unix_data, int which) { struct wimlib_unix_data_disk *p; p = inode_get_unix_data_disk(inode); if (!p) { p = inode_add_tagged_item(inode, TAG_WIMLIB_UNIX_DATA, sizeof(*p)); if (!p) return false; which = UNIX_DATA_ALL; } if (which & UNIX_DATA_UID) p->uid = cpu_to_le32(unix_data->uid); if (which & UNIX_DATA_GID) p->gid = cpu_to_le32(unix_data->gid); if (which & UNIX_DATA_MODE) p->mode = cpu_to_le32(unix_data->mode); if (which & UNIX_DATA_RDEV) p->rdev = cpu_to_le32(unix_data->rdev); return true; } wimlib-1.13.1/src/dentry.c0000644000175000017500000015713213230712763012256 00000000000000/* * dentry.c - see description below */ /* * Copyright (C) 2012-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ /* * This file contains logic to deal with WIM directory entries, or "dentries": * * - Reading a dentry tree from a metadata resource in a WIM file * - Writing a dentry tree to a metadata resource in a WIM file * - Iterating through a tree of WIM dentries * - Path lookup: translating a path into a WIM dentry or inode * - Creating, modifying, and deleting WIM dentries * * Notes: * * - A WIM file can contain multiple images, each of which has an independent * tree of dentries. "On disk", the dentry tree for an image is stored in * the "metadata resource" for that image. * * - Multiple dentries in an image may correspond to the same inode, or "file". * When this occurs, it means that the file has multiple names, or "hard * links". A dentry is not a file, but rather the name of a file! * * - Inodes are not represented explicitly in the WIM file format. Instead, * the metadata resource provides a "hard link group ID" for each dentry. * wimlib handles pulling out actual inodes from this information, but this * occurs in inode_fixup.c and not in this file. * * - wimlib does not allow *directory* hard links, so a WIM image really does * have a *tree* of dentries (and not an arbitrary graph of dentries). * * - wimlib supports both case-sensitive and case-insensitive path lookups. * The implementation uses a single in-memory index per directory, using a * collation order like that used by NTFS; see collate_dentry_names(). * * - Multiple dentries in a directory might have the same case-insensitive * name. But wimlib enforces that at most one dentry in a directory can have * a given case-sensitive name. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include "wimlib/assert.h" #include "wimlib/dentry.h" #include "wimlib/inode.h" #include "wimlib/encoding.h" #include "wimlib/endianness.h" #include "wimlib/metadata.h" #include "wimlib/paths.h" /* On-disk format of a WIM dentry (directory entry), located in the metadata * resource for a WIM image. */ struct wim_dentry_on_disk { /* Length of this directory entry in bytes, not including any extra * stream entries. Should be a multiple of 8 so that the following * dentry or extra stream entry is aligned on an 8-byte boundary. (If * not, wimlib will round it up.) It must be at least as long as the * fixed-length fields of the dentry (WIM_DENTRY_DISK_SIZE), plus the * lengths of the file name and/or short name if present, plus the size * of any "extra" data. * * It is also possible for this field to be 0. This case indicates the * end of a list of sibling entries in a directory. It also means the * real length is 8, because the dentry included only the length field, * but that takes up 8 bytes. */ le64 length; /* File attributes for the file or directory. This is a bitwise OR of * the FILE_ATTRIBUTE_* constants and should correspond to the value * retrieved by GetFileAttributes() on Windows. */ le32 attributes; /* A value that specifies the security descriptor for this file or * directory. If 0xFFFFFFFF, the file or directory has no security * descriptor. Otherwise, it is a 0-based index into the WIM image's * table of security descriptors (see: `struct wim_security_data') */ le32 security_id; /* Offset, in bytes, from the start of the uncompressed metadata * resource of this directory's child directory entries, or 0 if this * directory entry does not correspond to a directory or otherwise does * not have any children. */ le64 subdir_offset; /* Reserved fields */ le64 unused_1; le64 unused_2; /* Creation time, last access time, and last write time, in * 100-nanosecond intervals since 12:00 a.m UTC January 1, 1601. They * should correspond to the times gotten by calling GetFileTime() on * Windows. */ le64 creation_time; le64 last_access_time; le64 last_write_time; /* * Usually this is the SHA-1 message digest of the file's "contents" * (the unnamed data stream). * * If the file has FILE_ATTRIBUTE_REPARSE_POINT set, then this is * instead usually the SHA-1 message digest of the uncompressed reparse * point data. * * However, there are some special rules that need to be applied to * interpret this field correctly when extra stream entries are present. * See the code for details. */ u8 default_hash[SHA1_HASH_SIZE]; /* Unknown field (maybe accidental padding) */ le32 unknown_0x54; /* * The following 8-byte union contains either information about the * reparse point (for files with FILE_ATTRIBUTE_REPARSE_POINT set), or * the "hard link group ID" (for other files). * * The reparse point information contains ReparseTag and ReparseReserved * from the header of the reparse point buffer. It also contains a flag * that indicates whether a reparse point fixup (for the target of an * absolute symbolic link or junction) was done or not. * * The "hard link group ID" is like an inode number; all dentries for * the same inode share the same value. See inode_fixup.c for more * information. * * Note that this union creates the limitation that reparse point files * cannot have multiple names (hard links). */ union { struct { le32 reparse_tag; le16 rp_reserved; le16 rp_flags; } _packed_attribute reparse; struct { le64 hard_link_group_id; } _packed_attribute nonreparse; }; /* Number of extra stream entries that directly follow this dentry * on-disk. */ le16 num_extra_streams; /* If nonzero, this is the length, in bytes, of this dentry's UTF-16LE * encoded short name (8.3 DOS-compatible name), excluding the null * terminator. If zero, then the long name of this dentry does not have * a corresponding short name (but this does not exclude the possibility * that another dentry for the same file has a short name). */ le16 short_name_nbytes; /* If nonzero, this is the length, in bytes, of this dentry's UTF-16LE * encoded "long" name, excluding the null terminator. If zero, then * this file has no long name. The root dentry should not have a long * name, but all other dentries in the image should have long names. */ le16 name_nbytes; /* Beginning of optional, variable-length fields */ /* If name_nbytes != 0, the next field will be the UTF-16LE encoded long * name. This will be null-terminated, so the size of this field will * really be name_nbytes + 2. */ /*utf16lechar name[];*/ /* If short_name_nbytes != 0, the next field will be the UTF-16LE * encoded short name. This will be null-terminated, so the size of * this field will really be short_name_nbytes + 2. */ /*utf16lechar short_name[];*/ /* If there is still space in the dentry (according to the 'length' * field) after 8-byte alignment, then the remaining space will be a * variable-length list of tagged metadata items. See tagged_items.c * for more information. */ /* u8 tagged_items[] _aligned_attribute(8); */ } _packed_attribute; /* If num_extra_streams != 0, then there are that many extra stream * entries following the dentry, starting on the next 8-byte aligned * boundary. They are not counted in the 'length' field of the dentry. */ /* On-disk format of an extra stream entry. This represents an extra NTFS-style * "stream" associated with the file, such as a named data stream. */ struct wim_extra_stream_entry_on_disk { /* Length of this extra stream entry, in bytes. This includes all * fixed-length fields, plus the name and null terminator if present, * and any needed padding such that the length is a multiple of 8. */ le64 length; /* Reserved field */ le64 reserved; /* SHA-1 message digest of this stream's uncompressed data, or all * zeroes if this stream's data is of zero length. */ u8 hash[SHA1_HASH_SIZE]; /* Length of this stream's name, in bytes and excluding the null * terminator; or 0 if this stream is unnamed. */ le16 name_nbytes; /* Stream name in UTF-16LE. It is @name_nbytes bytes long, excluding * the null terminator. There is a null terminator character if * @name_nbytes != 0; i.e., if this stream is named. */ utf16lechar name[]; } _packed_attribute; static void do_dentry_set_name(struct wim_dentry *dentry, utf16lechar *name, size_t name_nbytes) { FREE(dentry->d_name); dentry->d_name = name; dentry->d_name_nbytes = name_nbytes; if (dentry_has_short_name(dentry)) { FREE(dentry->d_short_name); dentry->d_short_name = NULL; dentry->d_short_name_nbytes = 0; } } /* * Set the name of a WIM dentry from a UTF-16LE string. * * This sets the long name of the dentry. The short name will automatically be * removed, since it may not be appropriate for the new long name. * * The @name string need not be null-terminated, since its length is specified * in @name_nbytes. * * If @name_nbytes is 0, both the long and short names of the dentry will be * removed. * * Only use this function on unlinked dentries, since it doesn't update the name * indices. For dentries that are currently linked into the tree, use * rename_wim_path(). * * Returns 0 or WIMLIB_ERR_NOMEM. */ int dentry_set_name_utf16le(struct wim_dentry *dentry, const utf16lechar *name, size_t name_nbytes) { utf16lechar *dup = NULL; if (name_nbytes) { dup = utf16le_dupz(name, name_nbytes); if (!dup) return WIMLIB_ERR_NOMEM; } do_dentry_set_name(dentry, dup, name_nbytes); return 0; } /* * Set the name of a WIM dentry from a 'tchar' string. * * This sets the long name of the dentry. The short name will automatically be * removed, since it may not be appropriate for the new long name. * * If @name is NULL or empty, both the long and short names of the dentry will * be removed. * * Only use this function on unlinked dentries, since it doesn't update the name * indices. For dentries that are currently linked into the tree, use * rename_wim_path(). * * Returns 0 or an error code resulting from a failed string conversion. */ int dentry_set_name(struct wim_dentry *dentry, const tchar *name) { utf16lechar *name_utf16le = NULL; size_t name_utf16le_nbytes = 0; int ret; if (name && *name) { ret = tstr_to_utf16le(name, tstrlen(name) * sizeof(tchar), &name_utf16le, &name_utf16le_nbytes); if (ret) return ret; } do_dentry_set_name(dentry, name_utf16le, name_utf16le_nbytes); return 0; } /* Calculate the minimum unaligned length, in bytes, of an on-disk WIM dentry * that has names of the specified lengths. (Zero length means the * corresponding name actually does not exist.) The returned value excludes * tagged metadata items as well as any extra stream entries that may need to * follow the dentry. */ static size_t dentry_min_len_with_names(u16 name_nbytes, u16 short_name_nbytes) { size_t length = sizeof(struct wim_dentry_on_disk); if (name_nbytes) length += (u32)name_nbytes + 2; if (short_name_nbytes) length += (u32)short_name_nbytes + 2; return length; } /* Return the length, in bytes, required for the specified stream on-disk, when * represented as an extra stream entry. */ static size_t stream_out_total_length(const struct wim_inode_stream *strm) { /* Account for the fixed length portion */ size_t len = sizeof(struct wim_extra_stream_entry_on_disk); /* For named streams, account for the variable-length name. */ if (stream_is_named(strm)) len += utf16le_len_bytes(strm->stream_name) + 2; /* Account for any necessary padding to the next 8-byte boundary. */ return ALIGN(len, 8); } /* * Calculate the total number of bytes that will be consumed when a dentry is * written. This includes the fixed-length portion of the dentry, the name * fields, any tagged metadata items, and any extra stream entries. This also * includes all alignment bytes. */ size_t dentry_out_total_length(const struct wim_dentry *dentry) { const struct wim_inode *inode = dentry->d_inode; size_t len; len = dentry_min_len_with_names(dentry->d_name_nbytes, dentry->d_short_name_nbytes); len = ALIGN(len, 8); if (inode->i_extra) len += ALIGN(inode->i_extra->size, 8); if (!(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)) { /* * Extra stream entries: * * - Use one extra stream entry for each named data stream * - Use one extra stream entry for the unnamed data stream when there is either: * - a reparse point stream * - at least one named data stream (for Windows PE bug workaround) * - Use one extra stream entry for the reparse point stream if there is one */ bool have_named_data_stream = false; bool have_reparse_point_stream = false; for (unsigned i = 0; i < inode->i_num_streams; i++) { const struct wim_inode_stream *strm = &inode->i_streams[i]; if (stream_is_named_data_stream(strm)) { len += stream_out_total_length(strm); have_named_data_stream = true; } else if (strm->stream_type == STREAM_TYPE_REPARSE_POINT) { wimlib_assert(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT); have_reparse_point_stream = true; } } if (have_named_data_stream || have_reparse_point_stream) { if (have_reparse_point_stream) len += ALIGN(sizeof(struct wim_extra_stream_entry_on_disk), 8); len += ALIGN(sizeof(struct wim_extra_stream_entry_on_disk), 8); } } return len; } /* Internal version of for_dentry_in_tree() that omits the NULL check */ static int do_for_dentry_in_tree(struct wim_dentry *dentry, int (*visitor)(struct wim_dentry *, void *), void *arg) { int ret; struct wim_dentry *child; ret = (*visitor)(dentry, arg); if (unlikely(ret)) return ret; for_dentry_child(child, dentry) { ret = do_for_dentry_in_tree(child, visitor, arg); if (unlikely(ret)) return ret; } return 0; } /* Internal version of for_dentry_in_tree_depth() that omits the NULL check */ static int do_for_dentry_in_tree_depth(struct wim_dentry *dentry, int (*visitor)(struct wim_dentry *, void *), void *arg) { int ret; struct wim_dentry *child; for_dentry_child_postorder(child, dentry) { ret = do_for_dentry_in_tree_depth(child, visitor, arg); if (unlikely(ret)) return ret; } return unlikely((*visitor)(dentry, arg)); } /* * Call a function on all dentries in a tree. * * @arg will be passed as the second argument to each invocation of @visitor. * * This function does a pre-order traversal --- that is, a parent will be * visited before its children. Furthermore, siblings will be visited in their * collation order. * * It is safe to pass NULL for @root, which means that the dentry tree is empty. * In this case, this function does nothing. * * @visitor must not modify the structure of the dentry tree during the * traversal. * * The return value will be 0 if all calls to @visitor returned 0. Otherwise, * the return value will be the first nonzero value returned by @visitor. */ int for_dentry_in_tree(struct wim_dentry *root, int (*visitor)(struct wim_dentry *, void *), void *arg) { if (unlikely(!root)) return 0; return do_for_dentry_in_tree(root, visitor, arg); } /* Like for_dentry_in_tree(), but do a depth-first traversal of the dentry tree. * That is, the visitor function will be called on a dentry's children before * itself. It will be safe to free a dentry when visiting it. */ static int for_dentry_in_tree_depth(struct wim_dentry *root, int (*visitor)(struct wim_dentry *, void *), void *arg) { if (unlikely(!root)) return 0; return do_for_dentry_in_tree_depth(root, visitor, arg); } /* * Calculate the full path to @dentry within the WIM image, if not already done. * * The full name will be saved in the cached value 'dentry->d_full_path'. * * Whenever possible, use dentry_full_path() instead of calling this and * accessing d_full_path directly. * * Returns 0 or an error code resulting from a failed string conversion. */ int calculate_dentry_full_path(struct wim_dentry *dentry) { size_t ulen; const struct wim_dentry *d; if (dentry->d_full_path) return 0; ulen = 0; d = dentry; do { ulen += d->d_name_nbytes / sizeof(utf16lechar); ulen++; d = d->d_parent; /* assumes d == d->d_parent for root */ } while (!dentry_is_root(d)); utf16lechar ubuf[ulen]; utf16lechar *p = &ubuf[ulen]; d = dentry; do { p -= d->d_name_nbytes / sizeof(utf16lechar); if (d->d_name_nbytes) memcpy(p, d->d_name, d->d_name_nbytes); *--p = cpu_to_le16(WIM_PATH_SEPARATOR); d = d->d_parent; /* assumes d == d->d_parent for root */ } while (!dentry_is_root(d)); wimlib_assert(p == ubuf); return utf16le_to_tstr(ubuf, ulen * sizeof(utf16lechar), &dentry->d_full_path, NULL); } /* * Return the full path to the @dentry within the WIM image, or NULL if the full * path could not be determined due to a string conversion error. * * The returned memory will be cached in the dentry, so the caller is not * responsible for freeing it. */ tchar * dentry_full_path(struct wim_dentry *dentry) { calculate_dentry_full_path(dentry); return dentry->d_full_path; } static int dentry_calculate_subdir_offset(struct wim_dentry *dentry, void *_subdir_offset_p) { if (dentry_is_directory(dentry)) { u64 *subdir_offset_p = _subdir_offset_p; struct wim_dentry *child; /* Set offset of directory's child dentries */ dentry->d_subdir_offset = *subdir_offset_p; /* Account for child dentries */ for_dentry_child(child, dentry) *subdir_offset_p += dentry_out_total_length(child); /* Account for end-of-directory entry */ *subdir_offset_p += 8; } else { /* Not a directory; set the subdir offset to 0 */ dentry->d_subdir_offset = 0; } return 0; } /* * Calculate the subdir offsets for a dentry tree, in preparation of writing * that dentry tree to a metadata resource. * * The subdir offset of each dentry is the offset in the uncompressed metadata * resource at which its child dentries begin, or 0 if that dentry has no * children. * * The caller must initialize *subdir_offset_p to the first subdir offset that * is available to use after the root dentry is written. * * When this function returns, *subdir_offset_p will have been advanced past the * size needed for the dentry tree within the uncompressed metadata resource. */ void calculate_subdir_offsets(struct wim_dentry *root, u64 *subdir_offset_p) { for_dentry_in_tree(root, dentry_calculate_subdir_offset, subdir_offset_p); } static int dentry_compare_names(const struct wim_dentry *d1, const struct wim_dentry *d2, bool ignore_case) { return cmp_utf16le_strings(d1->d_name, d1->d_name_nbytes / 2, d2->d_name, d2->d_name_nbytes / 2, ignore_case); } /* * Collate (compare) the long filenames of two dentries. This first compares * the names ignoring case, then falls back to a case-sensitive comparison if * the names are the same ignoring case. */ static int collate_dentry_names(const struct avl_tree_node *n1, const struct avl_tree_node *n2) { const struct wim_dentry *d1, *d2; int res; d1 = avl_tree_entry(n1, struct wim_dentry, d_index_node); d2 = avl_tree_entry(n2, struct wim_dentry, d_index_node); res = dentry_compare_names(d1, d2, true); if (res) return res; return dentry_compare_names(d1, d2, false); } /* Default case sensitivity behavior for searches with * WIMLIB_CASE_PLATFORM_DEFAULT specified. This can be modified by passing * WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE or * WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE to wimlib_global_init(). */ bool default_ignore_case = #ifdef __WIN32__ true #else false #endif ; /* * Find the dentry within the given directory that has the given UTF-16LE * filename. Return it if found, otherwise return NULL. This has configurable * case sensitivity, and @name need not be null-terminated. */ struct wim_dentry * get_dentry_child_with_utf16le_name(const struct wim_dentry *dir, const utf16lechar *name, size_t name_nbytes, CASE_SENSITIVITY_TYPE case_type) { struct wim_dentry wanted; struct avl_tree_node *cur = dir->d_inode->i_children; struct wim_dentry *ci_match = NULL; wanted.d_name = (utf16lechar *)name; wanted.d_name_nbytes = name_nbytes; if (unlikely(wanted.d_name_nbytes != name_nbytes)) return NULL; /* overflow */ /* Note: we can't use avl_tree_lookup_node() here because we need to * save case-insensitive matches. */ while (cur) { struct wim_dentry *child; int res; child = avl_tree_entry(cur, struct wim_dentry, d_index_node); res = dentry_compare_names(&wanted, child, true); if (!res) { /* case-insensitive match found */ ci_match = child; res = dentry_compare_names(&wanted, child, false); if (!res) return child; /* case-sensitive match found */ } if (res < 0) cur = cur->left; else cur = cur->right; } /* No case-sensitive match; use a case-insensitive match if possible. */ if (!will_ignore_case(case_type)) return NULL; if (ci_match) { size_t num_other_ci_matches = 0; struct wim_dentry *other_ci_match, *d; dentry_for_each_ci_match(d, ci_match) { num_other_ci_matches++; other_ci_match = d; } if (num_other_ci_matches != 0) { WARNING("Result of case-insensitive lookup is ambiguous\n" " (returning \"%"TS"\" of %zu " "possible files, including \"%"TS"\")", dentry_full_path(ci_match), num_other_ci_matches, dentry_full_path(other_ci_match)); } } return ci_match; } /* * Find the dentry within the given directory that has the given 'tstr' * filename. If the filename was successfully converted to UTF-16LE and the * dentry was found, return it; otherwise return NULL. This has configurable * case sensitivity. */ struct wim_dentry * get_dentry_child_with_name(const struct wim_dentry *dir, const tchar *name, CASE_SENSITIVITY_TYPE case_type) { int ret; const utf16lechar *name_utf16le; size_t name_utf16le_nbytes; struct wim_dentry *child; ret = tstr_get_utf16le_and_len(name, &name_utf16le, &name_utf16le_nbytes); if (ret) return NULL; child = get_dentry_child_with_utf16le_name(dir, name_utf16le, name_utf16le_nbytes, case_type); tstr_put_utf16le(name_utf16le); return child; } /* This is the UTF-16LE version of get_dentry(), currently private to this file * because no one needs it besides get_dentry(). */ static struct wim_dentry * get_dentry_utf16le(WIMStruct *wim, const utf16lechar *path, CASE_SENSITIVITY_TYPE case_type) { struct wim_dentry *cur_dentry; const utf16lechar *name_start, *name_end; /* Start with the root directory of the image. Note: this will be NULL * if an image has been added directly with wimlib_add_empty_image() but * no files have been added yet; in that case we fail with ENOENT. */ cur_dentry = wim_get_current_root_dentry(wim); name_start = path; for (;;) { if (cur_dentry == NULL) { errno = ENOENT; return NULL; } if (*name_start && !dentry_is_directory(cur_dentry)) { errno = ENOTDIR; return NULL; } while (*name_start == cpu_to_le16(WIM_PATH_SEPARATOR)) name_start++; if (!*name_start) return cur_dentry; name_end = name_start; do { ++name_end; } while (*name_end != cpu_to_le16(WIM_PATH_SEPARATOR) && *name_end); cur_dentry = get_dentry_child_with_utf16le_name(cur_dentry, name_start, (u8*)name_end - (u8*)name_start, case_type); name_start = name_end; } } /* * WIM path lookup: translate a path in the currently selected WIM image to the * corresponding dentry, if it exists. * * @wim * The WIMStruct for the WIM. The search takes place in the currently * selected image. * * @path * The path to look up, given relative to the root of the WIM image. * Characters with value WIM_PATH_SEPARATOR are taken to be path * separators. Leading path separators are ignored, whereas one or more * trailing path separators cause the path to only match a directory. * * @case_type * The case-sensitivity behavior of this function, as one of the following * constants: * * - WIMLIB_CASE_SENSITIVE: Perform the search case sensitively. This means * that names must match exactly. * * - WIMLIB_CASE_INSENSITIVE: Perform the search case insensitively. This * means that names are considered to match if they are equal when * transformed to upper case. If a path component matches multiple names * case-insensitively, the name that matches the path component * case-sensitively is chosen, if existent; otherwise one * case-insensitively matching name is chosen arbitrarily. * * - WIMLIB_CASE_PLATFORM_DEFAULT: Perform either case-sensitive or * case-insensitive search, depending on the value of the global variable * default_ignore_case. * * In any case, no Unicode normalization is done before comparing strings. * * Returns a pointer to the dentry that is the result of the lookup, or NULL if * no such dentry exists. If NULL is returned, errno is set to one of the * following values: * * ENOTDIR if one of the path components used as a directory existed but * was not, in fact, a directory. * * ENOENT otherwise. * * Additional notes: * * - This function does not consider a reparse point to be a directory, even * if it has FILE_ATTRIBUTE_DIRECTORY set. * * - This function does not dereference symbolic links or junction points * when performing the search. * * - Since this function ignores leading slashes, the empty path is valid and * names the root directory of the WIM image. * * - An image added with wimlib_add_empty_image() does not have a root * directory yet, and this function will fail with ENOENT for any path on * such an image. */ struct wim_dentry * get_dentry(WIMStruct *wim, const tchar *path, CASE_SENSITIVITY_TYPE case_type) { int ret; const utf16lechar *path_utf16le; struct wim_dentry *dentry; ret = tstr_get_utf16le(path, &path_utf16le); if (ret) return NULL; dentry = get_dentry_utf16le(wim, path_utf16le, case_type); tstr_put_utf16le(path_utf16le); return dentry; } /* Modify @path, which is a null-terminated string @len 'tchars' in length, * in-place to produce the path to its parent directory. */ static void to_parent_name(tchar *path, size_t len) { ssize_t i = (ssize_t)len - 1; while (i >= 0 && path[i] == WIM_PATH_SEPARATOR) i--; while (i >= 0 && path[i] != WIM_PATH_SEPARATOR) i--; while (i >= 0 && path[i] == WIM_PATH_SEPARATOR) i--; path[i + 1] = T('\0'); } /* Similar to get_dentry(), but returns the dentry named by @path with the last * component stripped off. * * Note: The returned dentry is NOT guaranteed to be a directory. */ struct wim_dentry * get_parent_dentry(WIMStruct *wim, const tchar *path, CASE_SENSITIVITY_TYPE case_type) { size_t path_len = tstrlen(path); tchar buf[path_len + 1]; tmemcpy(buf, path, path_len + 1); to_parent_name(buf, path_len); return get_dentry(wim, buf, case_type); } /* * Create an unlinked dentry. * * @name specifies the long name to give the new dentry. If NULL or empty, the * new dentry will be given no long name. * * The new dentry will have no short name and no associated inode. * * On success, returns 0 and a pointer to the new, allocated dentry is stored in * *dentry_ret. On failure, returns WIMLIB_ERR_NOMEM or an error code resulting * from a failed string conversion. */ static int new_dentry(const tchar *name, struct wim_dentry **dentry_ret) { struct wim_dentry *dentry; int ret; dentry = CALLOC(1, sizeof(struct wim_dentry)); if (!dentry) return WIMLIB_ERR_NOMEM; if (name && *name) { ret = dentry_set_name(dentry, name); if (ret) { FREE(dentry); return ret; } } dentry->d_parent = dentry; *dentry_ret = dentry; return 0; } /* Like new_dentry(), but also allocate an inode and associate it with the * dentry. If set_timestamps=true, the timestamps for the inode will be set to * the current time; otherwise, they will be left 0. */ int new_dentry_with_new_inode(const tchar *name, bool set_timestamps, struct wim_dentry **dentry_ret) { struct wim_dentry *dentry; struct wim_inode *inode; int ret; ret = new_dentry(name, &dentry); if (ret) return ret; inode = new_inode(dentry, set_timestamps); if (!inode) { free_dentry(dentry); return WIMLIB_ERR_NOMEM; } *dentry_ret = dentry; return 0; } /* Like new_dentry(), but also associate the new dentry with the specified inode * and acquire a reference to each of the inode's blobs. */ int new_dentry_with_existing_inode(const tchar *name, struct wim_inode *inode, struct wim_dentry **dentry_ret) { int ret = new_dentry(name, dentry_ret); if (ret) return ret; d_associate(*dentry_ret, inode); inode_ref_blobs(inode); return 0; } /* Create an unnamed dentry with a new inode for a directory with the default * metadata. */ int new_filler_directory(struct wim_dentry **dentry_ret) { int ret; struct wim_dentry *dentry; ret = new_dentry_with_new_inode(NULL, true, &dentry); if (ret) return ret; /* Leave the inode number as 0; this is allowed for non * hard-linked files. */ dentry->d_inode->i_attributes = FILE_ATTRIBUTE_DIRECTORY; *dentry_ret = dentry; return 0; } /* * Free a WIM dentry. * * In addition to freeing the dentry itself, this disassociates the dentry from * its inode. If the inode is no longer in use, it will be freed as well. */ void free_dentry(struct wim_dentry *dentry) { if (dentry) { d_disassociate(dentry); FREE(dentry->d_name); FREE(dentry->d_short_name); FREE(dentry->d_full_path); FREE(dentry); } } static int do_free_dentry(struct wim_dentry *dentry, void *_ignore) { free_dentry(dentry); return 0; } static int do_free_dentry_and_unref_blobs(struct wim_dentry *dentry, void *blob_table) { inode_unref_blobs(dentry->d_inode, blob_table); free_dentry(dentry); return 0; } /* * Free all dentries in a tree. * * @root: * The root of the dentry tree to free. If NULL, this function has no * effect. * * @blob_table: * A pointer to the blob table for the WIM, or NULL if not specified. If * specified, this function will decrement the reference counts of the * blobs referenced by the dentries. * * This function also releases references to the corresponding inodes. * * This function does *not* unlink @root from its parent directory, if it has * one. If @root has a parent, the caller must unlink @root before calling this * function. */ void free_dentry_tree(struct wim_dentry *root, struct blob_table *blob_table) { int (*f)(struct wim_dentry *, void *); if (blob_table) f = do_free_dentry_and_unref_blobs; else f = do_free_dentry; for_dentry_in_tree_depth(root, f, blob_table); } /* * Return the first dentry in the list of dentries which have the same * case-insensitive name as the one given. */ struct wim_dentry * dentry_get_first_ci_match(struct wim_dentry *dentry) { struct wim_dentry *ci_match = dentry; for (;;) { struct avl_tree_node *node; struct wim_dentry *prev; node = avl_tree_prev_in_order(&ci_match->d_index_node); if (!node) break; prev = avl_tree_entry(node, struct wim_dentry, d_index_node); if (dentry_compare_names(prev, dentry, true)) break; ci_match = prev; } if (ci_match == dentry) return dentry_get_next_ci_match(dentry, dentry); return ci_match; } /* * Return the next dentry in the list of dentries which have the same * case-insensitive name as the one given. */ struct wim_dentry * dentry_get_next_ci_match(struct wim_dentry *dentry, struct wim_dentry *ci_match) { do { struct avl_tree_node *node; node = avl_tree_next_in_order(&ci_match->d_index_node); if (!node) return NULL; ci_match = avl_tree_entry(node, struct wim_dentry, d_index_node); } while (ci_match == dentry); if (dentry_compare_names(ci_match, dentry, true)) return NULL; return ci_match; } /* * Link a dentry into a directory. * * @parent: * The directory into which to link the dentry. * * @child: * The dentry to link into the directory. It must be currently unlinked. * * Returns NULL if successful; or, if @parent already contains a dentry with the * same case-sensitive name as @child, then a pointer to this duplicate dentry * is returned. */ struct wim_dentry * dentry_add_child(struct wim_dentry *parent, struct wim_dentry *child) { struct wim_inode *dir = parent->d_inode; struct avl_tree_node *duplicate; wimlib_assert(parent != child); wimlib_assert(inode_is_directory(dir)); duplicate = avl_tree_insert(&dir->i_children, &child->d_index_node, collate_dentry_names); if (duplicate) return avl_tree_entry(duplicate, struct wim_dentry, d_index_node); child->d_parent = parent; return NULL; } /* Unlink a dentry from its parent directory. */ void unlink_dentry(struct wim_dentry *dentry) { /* Do nothing if the dentry is root or it's already unlinked. Not * actually necessary based on the current callers, but we do the check * here to be safe. */ if (unlikely(dentry->d_parent == dentry)) return; avl_tree_remove(&dentry->d_parent->d_inode->i_children, &dentry->d_index_node); /* Not actually necessary, but to be safe don't retain the now-obsolete * parent pointer. */ dentry->d_parent = dentry; } static int read_extra_data(const u8 *p, const u8 *end, struct wim_inode *inode) { while (((uintptr_t)p & 7) && p < end) p++; if (unlikely(p < end)) { inode->i_extra = MALLOC(sizeof(struct wim_inode_extra) + end - p); if (!inode->i_extra) return WIMLIB_ERR_NOMEM; inode->i_extra->size = end - p; memcpy(inode->i_extra->data, p, end - p); } return 0; } /* * Set the type of each stream for an encrypted file. * * All data streams of the encrypted file should have been packed into a single * stream in the format provided by ReadEncryptedFileRaw() on Windows. We * assign this stream type STREAM_TYPE_EFSRPC_RAW_DATA. * * Encrypted files can't have a reparse point stream. In the on-disk NTFS * format they can, but as far as I know the reparse point stream of an * encrypted file can't be stored in the WIM format in a way that's compatible * with WIMGAPI, nor is there even any way for it to be read or written on * Windows when the process does not have access to the file encryption key. */ static void assign_stream_types_encrypted(struct wim_inode *inode) { for (unsigned i = 0; i < inode->i_num_streams; i++) { struct wim_inode_stream *strm = &inode->i_streams[i]; if (!stream_is_named(strm) && !is_zero_hash(strm->_stream_hash)) { strm->stream_type = STREAM_TYPE_EFSRPC_RAW_DATA; return; } } } /* * Set the type of each stream for an unencrypted file. * * There will be an unnamed data stream, a reparse point stream, or both an * unnamed data stream and a reparse point stream. In addition, there may be * named data streams. * * NOTE: if the file has a reparse point stream or at least one named data * stream, then WIMGAPI puts *all* streams in the extra stream entries and * leaves the default stream hash zeroed. wimlib now does the same. However, * for input we still support the default hash field being used, since wimlib * used to use it and MS software is somewhat accepting of it as well. */ static void assign_stream_types_unencrypted(struct wim_inode *inode) { bool found_reparse_point_stream = false; bool found_unnamed_data_stream = false; struct wim_inode_stream *unnamed_stream_with_zero_hash = NULL; for (unsigned i = 0; i < inode->i_num_streams; i++) { struct wim_inode_stream *strm = &inode->i_streams[i]; if (stream_is_named(strm)) { /* Named data stream */ strm->stream_type = STREAM_TYPE_DATA; } else if (i != 0 || !is_zero_hash(strm->_stream_hash)) { /* Unnamed stream in the extra stream entries, OR the * default stream in the dentry provided that it has a * nonzero hash. */ if ((inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) && !found_reparse_point_stream) { found_reparse_point_stream = true; strm->stream_type = STREAM_TYPE_REPARSE_POINT; } else if (!found_unnamed_data_stream) { found_unnamed_data_stream = true; strm->stream_type = STREAM_TYPE_DATA; } } else if (!unnamed_stream_with_zero_hash) { unnamed_stream_with_zero_hash = strm; } } if (unnamed_stream_with_zero_hash) { int type = STREAM_TYPE_UNKNOWN; if ((inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) && !found_reparse_point_stream) { type = STREAM_TYPE_REPARSE_POINT; } else if (!found_unnamed_data_stream) { type = STREAM_TYPE_DATA; } unnamed_stream_with_zero_hash->stream_type = type; } } /* * Read and interpret the collection of streams for the specified inode. */ static int setup_inode_streams(const u8 *p, const u8 *end, struct wim_inode *inode, unsigned num_extra_streams, const u8 *default_hash, u64 *offset_p) { const u8 *orig_p = p; inode->i_num_streams = 1 + num_extra_streams; if (unlikely(inode->i_num_streams > ARRAY_LEN(inode->i_embedded_streams))) { inode->i_streams = CALLOC(inode->i_num_streams, sizeof(inode->i_streams[0])); if (!inode->i_streams) return WIMLIB_ERR_NOMEM; } /* Use the default hash field for the first stream */ inode->i_streams[0].stream_name = (utf16lechar *)NO_STREAM_NAME; copy_hash(inode->i_streams[0]._stream_hash, default_hash); inode->i_streams[0].stream_type = STREAM_TYPE_UNKNOWN; inode->i_streams[0].stream_id = 0; /* Read the extra stream entries */ for (unsigned i = 1; i < inode->i_num_streams; i++) { struct wim_inode_stream *strm; const struct wim_extra_stream_entry_on_disk *disk_strm; u64 length; u16 name_nbytes; strm = &inode->i_streams[i]; strm->stream_id = i; /* Do we have at least the size of the fixed-length data we know * need? */ if ((end - p) < sizeof(struct wim_extra_stream_entry_on_disk)) return WIMLIB_ERR_INVALID_METADATA_RESOURCE; disk_strm = (const struct wim_extra_stream_entry_on_disk *)p; /* Read the length field */ length = ALIGN(le64_to_cpu(disk_strm->length), 8); /* Make sure the length field is neither so small it doesn't * include all the fixed-length data nor so large it overflows * the metadata resource buffer. */ if (length < sizeof(struct wim_extra_stream_entry_on_disk) || length > (end - p)) return WIMLIB_ERR_INVALID_METADATA_RESOURCE; /* Read the rest of the fixed-length data. */ copy_hash(strm->_stream_hash, disk_strm->hash); name_nbytes = le16_to_cpu(disk_strm->name_nbytes); /* If stream_name_nbytes != 0, the stream is named. */ if (name_nbytes != 0) { /* The name is encoded in UTF16-LE, which uses 2-byte * coding units, so the length of the name had better be * an even number of bytes. */ if (name_nbytes & 1) return WIMLIB_ERR_INVALID_METADATA_RESOURCE; /* Add the length of the stream name to get the length * we actually need to read. Make sure this isn't more * than the specified length of the entry. */ if (sizeof(struct wim_extra_stream_entry_on_disk) + name_nbytes > length) return WIMLIB_ERR_INVALID_METADATA_RESOURCE; strm->stream_name = utf16le_dupz(disk_strm->name, name_nbytes); if (!strm->stream_name) return WIMLIB_ERR_NOMEM; } else { strm->stream_name = (utf16lechar *)NO_STREAM_NAME; } strm->stream_type = STREAM_TYPE_UNKNOWN; p += length; } inode->i_next_stream_id = inode->i_num_streams; /* Now, assign a type to each stream. Unfortunately this requires * various hacks because stream types aren't explicitly provided in the * WIM on-disk format. */ if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)) assign_stream_types_encrypted(inode); else assign_stream_types_unencrypted(inode); *offset_p += p - orig_p; return 0; } /* Read a dentry, including all extra stream entries that follow it, from an * uncompressed metadata resource buffer. */ static int read_dentry(const u8 * restrict buf, size_t buf_len, u64 *offset_p, struct wim_dentry **dentry_ret) { u64 offset = *offset_p; u64 length; const u8 *p; const struct wim_dentry_on_disk *disk_dentry; struct wim_dentry *dentry; struct wim_inode *inode; u16 short_name_nbytes; u16 name_nbytes; u64 calculated_size; int ret; STATIC_ASSERT(sizeof(struct wim_dentry_on_disk) == WIM_DENTRY_DISK_SIZE); /* Before reading the whole dentry, we need to read just the length. * This is because a dentry of length 8 (that is, just the length field) * terminates the list of sibling directory entries. */ /* Check for buffer overrun. */ if (unlikely(offset + sizeof(u64) > buf_len || offset + sizeof(u64) < offset)) return WIMLIB_ERR_INVALID_METADATA_RESOURCE; /* Get pointer to the dentry data. */ p = &buf[offset]; disk_dentry = (const struct wim_dentry_on_disk*)p; /* Get dentry length. */ length = ALIGN(le64_to_cpu(disk_dentry->length), 8); /* Check for end-of-directory. */ if (length <= 8) { *dentry_ret = NULL; return 0; } /* Validate dentry length. */ if (unlikely(length < sizeof(struct wim_dentry_on_disk))) return WIMLIB_ERR_INVALID_METADATA_RESOURCE; /* Check for buffer overrun. */ if (unlikely(offset + length > buf_len || offset + length < offset)) return WIMLIB_ERR_INVALID_METADATA_RESOURCE; /* Allocate new dentry structure, along with a preliminary inode. */ ret = new_dentry_with_new_inode(NULL, false, &dentry); if (ret) return ret; inode = dentry->d_inode; /* Read more fields: some into the dentry, and some into the inode. */ inode->i_attributes = le32_to_cpu(disk_dentry->attributes); inode->i_security_id = le32_to_cpu(disk_dentry->security_id); dentry->d_subdir_offset = le64_to_cpu(disk_dentry->subdir_offset); inode->i_creation_time = le64_to_cpu(disk_dentry->creation_time); inode->i_last_access_time = le64_to_cpu(disk_dentry->last_access_time); inode->i_last_write_time = le64_to_cpu(disk_dentry->last_write_time); inode->i_unknown_0x54 = le32_to_cpu(disk_dentry->unknown_0x54); if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) { inode->i_reparse_tag = le32_to_cpu(disk_dentry->reparse.reparse_tag); inode->i_rp_reserved = le16_to_cpu(disk_dentry->reparse.rp_reserved); inode->i_rp_flags = le16_to_cpu(disk_dentry->reparse.rp_flags); /* Leave inode->i_ino at 0. Note: this means that WIM cannot * represent multiple hard links to a reparse point file. */ } else { inode->i_ino = le64_to_cpu(disk_dentry->nonreparse.hard_link_group_id); } /* Now onto reading the names. There are two of them: the (long) file * name, and the short name. */ short_name_nbytes = le16_to_cpu(disk_dentry->short_name_nbytes); name_nbytes = le16_to_cpu(disk_dentry->name_nbytes); if (unlikely((short_name_nbytes & 1) | (name_nbytes & 1))) { ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE; goto err_free_dentry; } /* We now know the length of the file name and short name. Make sure * the length of the dentry is large enough to actually hold them. */ calculated_size = dentry_min_len_with_names(name_nbytes, short_name_nbytes); if (unlikely(length < calculated_size)) { ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE; goto err_free_dentry; } /* Advance p to point past the base dentry, to the first name. */ p += sizeof(struct wim_dentry_on_disk); /* Read the filename if present. Note: if the filename is empty, there * is no null terminator following it. */ if (name_nbytes) { dentry->d_name = utf16le_dupz(p, name_nbytes); if (unlikely(!dentry->d_name)) { ret = WIMLIB_ERR_NOMEM; goto err_free_dentry; } dentry->d_name_nbytes = name_nbytes; p += (u32)name_nbytes + 2; } /* Read the short filename if present. Note: if there is no short * filename, there is no null terminator following it. */ if (short_name_nbytes) { dentry->d_short_name = utf16le_dupz(p, short_name_nbytes); if (unlikely(!dentry->d_short_name)) { ret = WIMLIB_ERR_NOMEM; goto err_free_dentry; } dentry->d_short_name_nbytes = short_name_nbytes; p += (u32)short_name_nbytes + 2; } /* Read extra data at end of dentry (but before extra stream entries). * This may contain tagged metadata items. */ ret = read_extra_data(p, &buf[offset + length], inode); if (ret) goto err_free_dentry; offset += length; /* Set up the inode's collection of streams. */ ret = setup_inode_streams(&buf[offset], &buf[buf_len], inode, le16_to_cpu(disk_dentry->num_extra_streams), disk_dentry->default_hash, &offset); if (ret) goto err_free_dentry; *offset_p = offset; /* Sets offset of next dentry in directory */ *dentry_ret = dentry; return 0; err_free_dentry: free_dentry(dentry); return ret; } static bool dentry_is_dot_or_dotdot(const struct wim_dentry *dentry) { if (dentry->d_name_nbytes <= 4) { if (dentry->d_name_nbytes == 4) { if (dentry->d_name[0] == cpu_to_le16('.') && dentry->d_name[1] == cpu_to_le16('.')) return true; } else if (dentry->d_name_nbytes == 2) { if (dentry->d_name[0] == cpu_to_le16('.')) return true; } } return false; } static bool dentry_contains_embedded_null(const struct wim_dentry *dentry) { for (unsigned i = 0; i < dentry->d_name_nbytes / 2; i++) if (dentry->d_name[i] == cpu_to_le16('\0')) return true; return false; } static bool should_ignore_dentry(struct wim_dentry *dir, const struct wim_dentry *dentry) { /* All dentries except the root must be named. */ if (!dentry_has_long_name(dentry)) { WARNING("Ignoring unnamed file in directory \"%"TS"\"", dentry_full_path(dir)); return true; } /* Don't allow files named "." or "..". Such filenames could be used in * path traversal attacks. */ if (dentry_is_dot_or_dotdot(dentry)) { WARNING("Ignoring file named \".\" or \"..\" in directory " "\"%"TS"\"", dentry_full_path(dir)); return true; } /* Don't allow filenames containing embedded null characters. Although * the null character is already considered an unsupported character for * extraction by all targets, it is probably a good idea to just forbid * such names entirely. */ if (dentry_contains_embedded_null(dentry)) { WARNING("Ignoring filename with embedded null character in " "directory \"%"TS"\"", dentry_full_path(dir)); return true; } return false; } static int read_dentry_tree_recursive(const u8 * restrict buf, size_t buf_len, struct wim_dentry * restrict dir, unsigned depth) { u64 cur_offset = dir->d_subdir_offset; /* Disallow extremely deep or cyclic directory structures */ if (unlikely(depth >= 16384)) { ERROR("Directory structure too deep!"); return WIMLIB_ERR_INVALID_METADATA_RESOURCE; } for (;;) { struct wim_dentry *child; struct wim_dentry *duplicate; int ret; /* Read next child of @dir. */ ret = read_dentry(buf, buf_len, &cur_offset, &child); if (ret) return ret; /* Check for end of directory. */ if (child == NULL) return 0; /* Ignore dentries with bad names. */ if (unlikely(should_ignore_dentry(dir, child))) { free_dentry(child); continue; } /* Link the child into the directory. */ duplicate = dentry_add_child(dir, child); if (unlikely(duplicate)) { /* We already found a dentry with this same * case-sensitive long name. Only keep the first one. */ WARNING("Ignoring duplicate file \"%"TS"\" " "(the WIM image already contains a file " "at that path with the exact same name)", dentry_full_path(duplicate)); free_dentry(child); continue; } /* If this child is a directory that itself has children, call * this procedure recursively. */ if (child->d_subdir_offset != 0) { if (likely(dentry_is_directory(child))) { ret = read_dentry_tree_recursive(buf, buf_len, child, depth + 1); if (ret) return ret; } else { WARNING("Ignoring children of " "non-directory file \"%"TS"\"", dentry_full_path(child)); } } } } /* * Read a tree of dentries from a WIM metadata resource. * * @buf: * Buffer containing an uncompressed WIM metadata resource. * * @buf_len: * Length of the uncompressed metadata resource, in bytes. * * @root_offset * Offset in the metadata resource of the root of the dentry tree. * * @root_ret: * On success, either NULL or a pointer to the root dentry is written to * this location. The former case only occurs in the unexpected case that * the tree began with an end-of-directory entry. * * Return values: * WIMLIB_ERR_SUCCESS (0) * WIMLIB_ERR_INVALID_METADATA_RESOURCE * WIMLIB_ERR_NOMEM */ int read_dentry_tree(const u8 *buf, size_t buf_len, u64 root_offset, struct wim_dentry **root_ret) { int ret; struct wim_dentry *root; ret = read_dentry(buf, buf_len, &root_offset, &root); if (ret) return ret; if (likely(root != NULL)) { if (unlikely(dentry_has_long_name(root) || dentry_has_short_name(root))) { WARNING("The root directory has a nonempty name; " "removing it."); dentry_set_name(root, NULL); } if (unlikely(!dentry_is_directory(root))) { ERROR("The root of the WIM image is not a directory!"); ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE; goto err_free_dentry_tree; } if (likely(root->d_subdir_offset != 0)) { ret = read_dentry_tree_recursive(buf, buf_len, root, 0); if (ret) goto err_free_dentry_tree; } } else { WARNING("The metadata resource has no directory entries; " "treating as an empty image."); } *root_ret = root; return 0; err_free_dentry_tree: free_dentry_tree(root, NULL); return ret; } static u8 * write_extra_stream_entry(u8 * restrict p, const utf16lechar * restrict name, const u8 * restrict hash) { struct wim_extra_stream_entry_on_disk *disk_strm = (struct wim_extra_stream_entry_on_disk *)p; u8 *orig_p = p; size_t name_nbytes; if (name == NO_STREAM_NAME) name_nbytes = 0; else name_nbytes = utf16le_len_bytes(name); disk_strm->reserved = 0; copy_hash(disk_strm->hash, hash); disk_strm->name_nbytes = cpu_to_le16(name_nbytes); p += sizeof(struct wim_extra_stream_entry_on_disk); if (name_nbytes != 0) p = mempcpy(p, name, name_nbytes + 2); /* Align to 8-byte boundary */ while ((uintptr_t)p & 7) *p++ = 0; disk_strm->length = cpu_to_le64(p - orig_p); return p; } /* * Write a WIM dentry to an output buffer. * * This includes any extra stream entries that may follow the dentry itself. * * @dentry: * The dentry to write. * * @p: * The memory location to which to write the data. * * Returns a pointer to the byte following the last written. */ static u8 * write_dentry(const struct wim_dentry * restrict dentry, u8 * restrict p) { const struct wim_inode *inode; struct wim_dentry_on_disk *disk_dentry; const u8 *orig_p; wimlib_assert(((uintptr_t)p & 7) == 0); /* 8 byte aligned */ orig_p = p; inode = dentry->d_inode; disk_dentry = (struct wim_dentry_on_disk*)p; disk_dentry->attributes = cpu_to_le32(inode->i_attributes); disk_dentry->security_id = cpu_to_le32(inode->i_security_id); disk_dentry->subdir_offset = cpu_to_le64(dentry->d_subdir_offset); disk_dentry->unused_1 = cpu_to_le64(0); disk_dentry->unused_2 = cpu_to_le64(0); disk_dentry->creation_time = cpu_to_le64(inode->i_creation_time); disk_dentry->last_access_time = cpu_to_le64(inode->i_last_access_time); disk_dentry->last_write_time = cpu_to_le64(inode->i_last_write_time); disk_dentry->unknown_0x54 = cpu_to_le32(inode->i_unknown_0x54); if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) { disk_dentry->reparse.reparse_tag = cpu_to_le32(inode->i_reparse_tag); disk_dentry->reparse.rp_reserved = cpu_to_le16(inode->i_rp_reserved); disk_dentry->reparse.rp_flags = cpu_to_le16(inode->i_rp_flags); } else { disk_dentry->nonreparse.hard_link_group_id = cpu_to_le64((inode->i_nlink == 1) ? 0 : inode->i_ino); } disk_dentry->short_name_nbytes = cpu_to_le16(dentry->d_short_name_nbytes); disk_dentry->name_nbytes = cpu_to_le16(dentry->d_name_nbytes); p += sizeof(struct wim_dentry_on_disk); wimlib_assert(dentry_is_root(dentry) != dentry_has_long_name(dentry)); if (dentry_has_long_name(dentry)) p = mempcpy(p, dentry->d_name, (u32)dentry->d_name_nbytes + 2); if (dentry_has_short_name(dentry)) p = mempcpy(p, dentry->d_short_name, (u32)dentry->d_short_name_nbytes + 2); /* Align to 8-byte boundary */ while ((uintptr_t)p & 7) *p++ = 0; if (inode->i_extra) { /* Extra tagged items --- not usually present. */ p = mempcpy(p, inode->i_extra->data, inode->i_extra->size); /* Align to 8-byte boundary */ while ((uintptr_t)p & 7) *p++ = 0; } disk_dentry->length = cpu_to_le64(p - orig_p); /* Streams */ if (unlikely(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)) { const struct wim_inode_stream *efs_strm; const u8 *efs_hash; efs_strm = inode_get_unnamed_stream(inode, STREAM_TYPE_EFSRPC_RAW_DATA); efs_hash = efs_strm ? stream_hash(efs_strm) : zero_hash; copy_hash(disk_dentry->default_hash, efs_hash); disk_dentry->num_extra_streams = cpu_to_le16(0); } else { /* * Extra stream entries: * * - Use one extra stream entry for each named data stream * - Use one extra stream entry for the unnamed data stream when there is either: * - a reparse point stream * - at least one named data stream (for Windows PE bug workaround) * - Use one extra stream entry for the reparse point stream if there is one */ bool have_named_data_stream = false; bool have_reparse_point_stream = false; const u8 *unnamed_data_stream_hash = zero_hash; const u8 *reparse_point_hash; for (unsigned i = 0; i < inode->i_num_streams; i++) { const struct wim_inode_stream *strm = &inode->i_streams[i]; if (strm->stream_type == STREAM_TYPE_DATA) { if (stream_is_named(strm)) have_named_data_stream = true; else unnamed_data_stream_hash = stream_hash(strm); } else if (strm->stream_type == STREAM_TYPE_REPARSE_POINT) { have_reparse_point_stream = true; reparse_point_hash = stream_hash(strm); } } if (unlikely(have_reparse_point_stream || have_named_data_stream)) { unsigned num_extra_streams = 0; copy_hash(disk_dentry->default_hash, zero_hash); if (have_reparse_point_stream) { p = write_extra_stream_entry(p, NO_STREAM_NAME, reparse_point_hash); num_extra_streams++; } p = write_extra_stream_entry(p, NO_STREAM_NAME, unnamed_data_stream_hash); num_extra_streams++; for (unsigned i = 0; i < inode->i_num_streams; i++) { const struct wim_inode_stream *strm = &inode->i_streams[i]; if (stream_is_named_data_stream(strm)) { p = write_extra_stream_entry(p, strm->stream_name, stream_hash(strm)); num_extra_streams++; } } wimlib_assert(num_extra_streams <= 0xFFFF); disk_dentry->num_extra_streams = cpu_to_le16(num_extra_streams); } else { copy_hash(disk_dentry->default_hash, unnamed_data_stream_hash); disk_dentry->num_extra_streams = cpu_to_le16(0); } } return p; } static int write_dir_dentries(struct wim_dentry *dir, void *_pp) { if (dir->d_subdir_offset != 0) { u8 **pp = _pp; u8 *p = *pp; struct wim_dentry *child; /* write child dentries */ for_dentry_child(child, dir) p = write_dentry(child, p); /* write end of directory entry */ *(u64*)p = 0; p += 8; *pp = p; } return 0; } /* * Write a directory tree to the metadata resource. * * @root: * The root of a dentry tree on which calculate_subdir_offsets() has been * called. This cannot be NULL; if the dentry tree is empty, the caller is * expected to first generate a dummy root directory. * * @p: * Pointer to a buffer with enough space for the dentry tree. This size * must have been obtained by calculate_subdir_offsets(). * * Returns a pointer to the byte following the last written. */ u8 * write_dentry_tree(struct wim_dentry *root, u8 *p) { /* write root dentry and end-of-directory entry following it */ p = write_dentry(root, p); *(u64*)p = 0; p += 8; /* write the rest of the dentry tree */ for_dentry_in_tree(root, write_dir_dentries, &p); return p; } wimlib-1.13.1/src/xpress_decompress.c0000644000175000017500000001354313160354225014513 00000000000000/* * xpress_decompress.c * * A decompressor for the XPRESS compression format (Huffman variant). */ /* * * Copyright (C) 2012-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ /* * The XPRESS compression format is an LZ77 and Huffman-code based algorithm. * That means it is fairly similar to LZX compression, but XPRESS is simpler, so * it is a little faster to compress and decompress. * * The XPRESS compression format is mostly documented in a file called "[MS-XCA] * Xpress Compression Algorithm". In the MSDN library, it can currently be * found under Open Specifications => Protocols => Windows Protocols => Windows * Server Protocols => [MS-XCA] Xpress Compression Algorithm". The format in * WIMs is specifically the algorithm labeled as the "LZ77+Huffman Algorithm" * (there apparently are some other versions of XPRESS as well). * * If you are already familiar with the LZ77 algorithm and Huffman coding, the * XPRESS format is fairly simple. The compressed data begins with 256 bytes * that contain 512 4-bit integers that are the lengths of the symbols in the * Huffman code used for match/literal headers. In contrast with more * complicated formats such as DEFLATE and LZX, this is the only Huffman code * that is used for the entirety of the XPRESS compressed data, and the codeword * lengths are not encoded with a pretree. * * The rest of the compressed data is Huffman-encoded symbols. Values 0 through * 255 represent the corresponding literal bytes. Values 256 through 511 * represent matches and may require extra bits or bytes to be read to get the * match offset and match length. * * The trickiest part is probably the way in which literal bytes for match * lengths are interleaved in the bitstream. * * Also, a caveat--- according to Microsoft's documentation for XPRESS, * * "Some implementation of the decompression algorithm expect an extra * symbol to mark the end of the data. Specifically, some implementations * fail during decompression if the Huffman symbol 256 is not found after * the actual data." * * This is the case with Microsoft's implementation in WIMGAPI, for example. So * although our implementation doesn't currently check for this extra symbol, * compressors would be wise to add it. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib/decompressor_ops.h" #include "wimlib/decompress_common.h" #include "wimlib/error.h" #include "wimlib/util.h" #include "wimlib/xpress_constants.h" /* This value is chosen for fast decompression. */ #define XPRESS_TABLEBITS 11 struct xpress_decompressor { union { DECODE_TABLE(decode_table, XPRESS_NUM_SYMBOLS, XPRESS_TABLEBITS, XPRESS_MAX_CODEWORD_LEN); u8 lens[XPRESS_NUM_SYMBOLS]; }; DECODE_TABLE_WORKING_SPACE(working_space, XPRESS_NUM_SYMBOLS, XPRESS_MAX_CODEWORD_LEN); } _aligned_attribute(DECODE_TABLE_ALIGNMENT); static int xpress_decompress(const void *restrict compressed_data, size_t compressed_size, void *restrict uncompressed_data, size_t uncompressed_size, void *restrict _d) { struct xpress_decompressor *d = _d; const u8 * const in_begin = compressed_data; u8 * const out_begin = uncompressed_data; u8 *out_next = out_begin; u8 * const out_end = out_begin + uncompressed_size; struct input_bitstream is; /* Read the Huffman codeword lengths. */ if (compressed_size < XPRESS_NUM_SYMBOLS / 2) return -1; for (int i = 0; i < XPRESS_NUM_SYMBOLS / 2; i++) { d->lens[2 * i + 0] = in_begin[i] & 0xf; d->lens[2 * i + 1] = in_begin[i] >> 4; } /* Build a decoding table for the Huffman code. */ if (make_huffman_decode_table(d->decode_table, XPRESS_NUM_SYMBOLS, XPRESS_TABLEBITS, d->lens, XPRESS_MAX_CODEWORD_LEN, d->working_space)) return -1; /* Decode the matches and literals. */ init_input_bitstream(&is, in_begin + XPRESS_NUM_SYMBOLS / 2, compressed_size - XPRESS_NUM_SYMBOLS / 2); while (out_next != out_end) { unsigned sym; unsigned log2_offset; u32 length; u32 offset; sym = read_huffsym(&is, d->decode_table, XPRESS_TABLEBITS, XPRESS_MAX_CODEWORD_LEN); if (sym < XPRESS_NUM_CHARS) { /* Literal */ *out_next++ = sym; } else { /* Match */ length = sym & 0xf; log2_offset = (sym >> 4) & 0xf; bitstream_ensure_bits(&is, 16); offset = ((u32)1 << log2_offset) | bitstream_pop_bits(&is, log2_offset); if (length == 0xf) { length += bitstream_read_byte(&is); if (length == 0xf + 0xff) length = bitstream_read_u16(&is); } length += XPRESS_MIN_MATCH_LEN; if (unlikely(lz_copy(length, offset, out_begin, out_next, out_end, XPRESS_MIN_MATCH_LEN))) return -1; out_next += length; } } return 0; } static int xpress_create_decompressor(size_t max_block_size, void **d_ret) { struct xpress_decompressor *d; if (max_block_size > XPRESS_MAX_OFFSET + 1) return WIMLIB_ERR_INVALID_PARAM; d = ALIGNED_MALLOC(sizeof(*d), DECODE_TABLE_ALIGNMENT); if (!d) return WIMLIB_ERR_NOMEM; *d_ret = d; return 0; } static void xpress_free_decompressor(void *_d) { ALIGNED_FREE(_d); } const struct decompressor_ops xpress_decompressor_ops = { .create_decompressor = xpress_create_decompressor, .decompress = xpress_decompress, .free_decompressor = xpress_free_decompressor, }; wimlib-1.13.1/src/file_io.c0000644000175000017500000001155313160354224012347 00000000000000/* * file_io.c - Helper functions for reading and writing to file descriptors. */ /* * Copyright (C) 2013 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include "wimlib/error.h" #include "wimlib/file_io.h" #include "wimlib/util.h" #ifdef __WIN32__ # include "wimlib/win32.h" # define read win32_read # define write win32_write # define pread win32_pread # define pwrite win32_pwrite #endif /* * Wrapper around read() that checks for errors and keeps retrying until all * requested bytes have been read or until end-of file has occurred. * * Return values: * WIMLIB_ERR_SUCCESS (0) * WIMLIB_ERR_READ (errno set) * WIMLIB_ERR_UNEXPECTED_END_OF_FILE (errno set to EINVAL) */ int full_read(struct filedes *fd, void *buf, size_t count) { while (count) { ssize_t ret = read(fd->fd, buf, count); if (unlikely(ret <= 0)) { if (ret == 0) { errno = EINVAL; return WIMLIB_ERR_UNEXPECTED_END_OF_FILE; } if (errno == EINTR) continue; return WIMLIB_ERR_READ; } buf += ret; count -= ret; fd->offset += ret; } return 0; } static int pipe_read(struct filedes *fd, void *buf, size_t count, off_t offset) { int ret; /* Verify the offset. */ if (offset < fd->offset) { ERROR("Can't seek backwards in pipe " "(offset %"PRIu64" => %"PRIu64").\n" " Make sure the WIM was captured as " "pipable.", fd->offset, offset); errno = ESPIPE; return WIMLIB_ERR_RESOURCE_ORDER; } /* Manually seek to the requested position. */ while (fd->offset != offset) { size_t bytes_to_read = min(offset - fd->offset, BUFFER_SIZE); u8 dummy[bytes_to_read]; ret = full_read(fd, dummy, bytes_to_read); if (ret) return ret; } /* Do the actual read. */ return full_read(fd, buf, count); } /* * Wrapper around pread() that checks for errors and keeps retrying until all * requested bytes have been read or until end-of file has occurred. This also * transparently handle reading from pipe files, but the caller needs to be sure * the requested offset is greater than or equal to the current offset, or else * WIMLIB_ERR_RESOURCE_ORDER will be returned. * * Return values: * WIMLIB_ERR_SUCCESS (0) * WIMLIB_ERR_READ (errno set) * WIMLIB_ERR_UNEXPECTED_END_OF_FILE (errno set to EINVAL) * WIMLIB_ERR_RESOURCE_ORDER (errno set to ESPIPE) */ int full_pread(struct filedes *fd, void *buf, size_t count, off_t offset) { if (fd->is_pipe) goto is_pipe; while (count) { ssize_t ret = pread(fd->fd, buf, count, offset); if (unlikely(ret <= 0)) { if (ret == 0) { errno = EINVAL; return WIMLIB_ERR_UNEXPECTED_END_OF_FILE; } if (errno == EINTR) continue; if (errno == ESPIPE) { fd->is_pipe = 1; goto is_pipe; } return WIMLIB_ERR_READ; } buf += ret; count -= ret; offset += ret; } return 0; is_pipe: return pipe_read(fd, buf, count, offset); } /* * Wrapper around write() that checks for errors and keeps retrying until all * requested bytes have been written. * * Return values: * WIMLIB_ERR_SUCCESS (0) * WIMLIB_ERR_WRITE (errno set) */ int full_write(struct filedes *fd, const void *buf, size_t count) { while (count) { ssize_t ret = write(fd->fd, buf, count); if (unlikely(ret < 0)) { if (errno == EINTR) continue; return WIMLIB_ERR_WRITE; } buf += ret; count -= ret; fd->offset += ret; } return 0; } /* * Wrapper around pwrite() that checks for errors and keeps retrying until all * requested bytes have been written. * * Return values: * WIMLIB_ERR_SUCCESS (0) * WIMLIB_ERR_WRITE (errno set) */ int full_pwrite(struct filedes *fd, const void *buf, size_t count, off_t offset) { while (count) { ssize_t ret = pwrite(fd->fd, buf, count, offset); if (unlikely(ret < 0)) { if (errno == EINTR) continue; return WIMLIB_ERR_WRITE; } buf += ret; count -= ret; offset += ret; } return 0; } off_t filedes_seek(struct filedes *fd, off_t offset) { if (fd->is_pipe) { errno = ESPIPE; return -1; } if (fd->offset != offset) { if (lseek(fd->fd, offset, SEEK_SET) == -1) return -1; fd->offset = offset; } return offset; } bool filedes_is_seekable(struct filedes *fd) { return !fd->is_pipe && lseek(fd->fd, 0, SEEK_CUR) != -1; } wimlib-1.13.1/src/xml.c0000644000175000017500000011611313160354225011540 00000000000000/* * xml.c * * Deals with the XML information in WIM files. Uses the C library libxml2. */ /* * Copyright (C) 2012-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include "wimlib/blob_table.h" #include "wimlib/dentry.h" #include "wimlib/encoding.h" #include "wimlib/error.h" #include "wimlib/file_io.h" #include "wimlib/metadata.h" #include "wimlib/resource.h" #include "wimlib/timestamp.h" #include "wimlib/xml.h" #include "wimlib/write.h" /* * A wrapper around a WIM file's XML document. The XML document contains * metadata about each image in the WIM file as well as metadata about the WIM * file itself. */ struct wim_xml_info { /* The parsed XML document as a libxml2 document tree */ xmlDocPtr doc; /* The root element of the document. This is a cached value, equal to * xmlDocGetRootElement(doc). */ xmlNode *root; /* A malloc()ed array containing a pointer to the IMAGE element for each * WIM image. The image with 1-based index 'i' is at index 'i - 1' in * this array. Note: these pointers are cached values, since they could * also be found by searching the document. */ xmlNode **images; /* The number of WIM images (the length of 'images') */ int image_count; #if TCHAR_IS_UTF16LE /* Temporary memory for UTF-8 => 'tchar' string translations. When an * API function needs to return a 'tchar' string, it uses one of these * array slots to hold the string and returns a pointer to it. */ tchar *strings[128]; size_t next_string_idx; size_t num_strings; #endif }; /*----------------------------------------------------------------------------* * Internal functions * *----------------------------------------------------------------------------*/ /* Iterate through the children of an xmlNode. */ #define node_for_each_child(parent, child) \ for (child = (parent)->children; child != NULL; child = child->next) /* Is the specified node an element of the specified name? */ static bool node_is_element(const xmlNode *node, const xmlChar *name) { return node->type == XML_ELEMENT_NODE && xmlStrEqual(node->name, name); } /* Retrieve a pointer to the UTF-8 text contents of the specified node, or NULL * if the node has no text contents. This assumes the simple case where the * node has a single TEXT child node. */ static const xmlChar * node_get_text(const xmlNode *node) { const xmlNode *child; if (!node) return NULL; node_for_each_child(node, child) if (child->type == XML_TEXT_NODE && child->content) return child->content; return NULL; } /* Retrieve an unsigned integer from the contents of the specified node, * decoding it using the specified base. If the node has no contents or does * not contain a valid number, returns 0. */ static u64 node_get_number(const xmlNode *node, int base) { const xmlChar *str = node_get_text(node); char *end; unsigned long long v; if (!str) return 0; v = strtoull(str, &end, base); if ((xmlChar *)end == str || *end || v >= UINT64_MAX) return 0; return v; } /* Retrieve the timestamp from a time node. This node should have child * elements HIGHPART and LOWPART; these elements will be used to construct a * Windows-style timestamp. */ static u64 node_get_timestamp(const xmlNode *node) { u64 timestamp = 0; xmlNode *child; if (!node) return 0; node_for_each_child(node, child) { if (node_is_element(child, "HIGHPART")) timestamp |= node_get_number(child, 16) << 32; else if (node_is_element(child, "LOWPART")) timestamp |= node_get_number(child, 16); } return timestamp; } static int tstr_get_utf8(const tchar *tstr, const xmlChar **utf8_ret) { #if TCHAR_IS_UTF16LE return utf16le_to_utf8(tstr, tstrlen(tstr) * sizeof(tchar), (char **)utf8_ret, NULL); #else *utf8_ret = (const xmlChar *)tstr; return 0; #endif } static void tstr_put_utf8(const xmlChar *utf8) { #if TCHAR_IS_UTF16LE FREE((char *)utf8); #endif } /* Retrieve the text contents of an XML element as a 'tchar' string. If not * found or if the text could not be translated, returns NULL. */ static const tchar * node_get_ttext(struct wim_xml_info *info, xmlNode *node) { const xmlChar *text = node_get_text(node); #if TCHAR_IS_UTF16LE tchar **ttext_p; if (!text) return NULL; ttext_p = &info->strings[info->next_string_idx]; if (info->num_strings >= ARRAY_LEN(info->strings)) { FREE(*ttext_p); *ttext_p = NULL; } if (utf8_to_tstr(text, strlen(text), ttext_p, NULL)) return NULL; if (info->num_strings < ARRAY_LEN(info->strings)) info->num_strings++; info->next_string_idx++; info->next_string_idx %= ARRAY_LEN(info->strings); return *ttext_p; #else return text; #endif } /* Unlink the specified node from its parent, then free it (recursively). */ static void unlink_and_free_tree(xmlNode *node) { xmlUnlinkNode(node); xmlFreeNode(node); } /* Unlink and free (recursively) all children of the specified node. */ static void unlink_and_free_children(xmlNode *node) { xmlNode *child; while ((child = node->last) != NULL) unlink_and_free_tree(child); } /* Add the new child element 'replacement' to 'parent', replacing any same-named * element that may already exist. */ static void node_replace_child_element(xmlNode *parent, xmlNode *replacement) { xmlNode *child; node_for_each_child(parent, child) { if (node_is_element(child, replacement->name)) { xmlReplaceNode(child, replacement); xmlFreeNode(child); return; } } xmlAddChild(parent, replacement); } /* Set the text contents of the specified element to the specified string, * replacing the existing contents (if any). The string is "raw" and is * permitted to contain characters that have special meaning in XML. */ static int node_set_text(xmlNode *node, const xmlChar *text) { xmlNode *text_node = xmlNewText(text); if (!text_node) return WIMLIB_ERR_NOMEM; unlink_and_free_children(node); xmlAddChild(node, text_node); return 0; } /* Like 'node_set_text()', but takes in a 'tchar' string. */ static int node_set_ttext(xmlNode *node, const tchar *ttext) { const xmlChar *text; int ret; ret = tstr_get_utf8(ttext, &text); if (ret) return ret; ret = node_set_text(node, text); tstr_put_utf8(text); return ret; } /* Create a new element containing text and optionally link it into a tree. */ static xmlNode * new_element_with_text(xmlNode *parent, const xmlChar *name, const xmlChar *text) { xmlNode *node; node = xmlNewNode(NULL, name); if (!node) return NULL; if (node_set_text(node, text)) { xmlFreeNode(node); return NULL; } if (parent) xmlAddChild(parent, node); return node; } /* Create a new element containing text and optionally link it into a tree. */ static int new_element_with_ttext(xmlNode *parent, const xmlChar *name, const tchar *ttext, xmlNode **node_ret) { const xmlChar *text; int ret; xmlNode *node; ret = tstr_get_utf8(ttext, &text); if (ret) return ret; node = new_element_with_text(parent, name, text); tstr_put_utf8(text); if (!node) return WIMLIB_ERR_NOMEM; if (node_ret) *node_ret = node; return 0; } /* Create a new timestamp element and optionally link it into a tree. */ static xmlNode * new_element_with_timestamp(xmlNode *parent, const xmlChar *name, u64 timestamp) { xmlNode *node; char buf[32]; node = xmlNewNode(NULL, name); if (!node) goto err; sprintf(buf, "0x%08"PRIX32, (u32)(timestamp >> 32)); if (!new_element_with_text(node, "HIGHPART", buf)) goto err; sprintf(buf, "0x%08"PRIX32, (u32)timestamp); if (!new_element_with_text(node, "LOWPART", buf)) goto err; if (parent) xmlAddChild(parent, node); return node; err: xmlFreeNode(node); return NULL; } /* Create a new number element and optionally link it into a tree. */ static xmlNode * new_element_with_u64(xmlNode *parent, const xmlChar *name, u64 value) { char buf[32]; sprintf(buf, "%"PRIu64, value); return new_element_with_text(parent, name, buf); } /* Allocate a 'struct wim_xml_info'. The caller is responsible for initializing * the document and the images array. */ static struct wim_xml_info * alloc_wim_xml_info(void) { struct wim_xml_info *info = MALLOC(sizeof(*info)); #if TCHAR_IS_UTF16LE if (info) { info->next_string_idx = 0; info->num_strings = 0; } #endif return info; } static bool parse_index(xmlChar **pp, u32 *index_ret) { xmlChar *p = *pp; u32 index = 0; *p++ = '\0'; /* overwrite '[' */ while (*p >= '0' && *p <= '9') { u32 n = (index * 10) + (*p++ - '0'); if (n < index) return false; index = n; } if (index == 0) return false; if (*p != ']') return false; p++; if (*p != '/' && *p != '\0') return false; *pp = p; *index_ret = index; return true; } static int do_xml_path_walk(xmlNode *node, const xmlChar *path, bool create, xmlNode **result_ret) { size_t n = strlen(path) + 1; xmlChar buf[n]; xmlChar *p; xmlChar c; *result_ret = NULL; if (!node) return 0; /* Copy the path to a temporary buffer. */ memcpy(buf, path, n); p = buf; if (*p == '/') goto bad_syntax; c = *p; while (c != '\0') { const xmlChar *name; xmlNode *child; u32 index = 1; /* We have another path component. */ /* Parse the element name. */ name = p; while (*p != '/' && *p != '\0' && *p != '[') p++; if (p == name) /* empty name? */ goto bad_syntax; /* Handle a bracketed index, if one was specified. */ if (*p == '[' && !parse_index(&p, &index)) goto bad_syntax; c = *p; *p = '\0'; /* Look for a matching child. */ node_for_each_child(node, child) if (node_is_element(child, name) && !--index) goto next_step; /* No child matched the path. If create=false, the lookup * failed. If create=true, create the needed element. */ if (!create) return 0; /* We can't create an element at index 'n' if indices 1...n-1 * didn't already exist. */ if (index != 1) return WIMLIB_ERR_INVALID_PARAM; child = xmlNewChild(node, NULL, name, NULL); if (!child) return WIMLIB_ERR_NOMEM; next_step: /* Continue to the next path component, if there is one. */ node = child; p++; } *result_ret = node; return 0; bad_syntax: ERROR("The XML path \"%s\" has invalid syntax.", path); return WIMLIB_ERR_INVALID_PARAM; } /* Retrieve the XML element, if any, at the specified 'path'. This supports a * simple filesystem-like syntax. If the element was found, returns a pointer * to it; otherwise returns NULL. */ static xmlNode * xml_get_node_by_path(xmlNode *root, const xmlChar *path) { xmlNode *node; do_xml_path_walk(root, path, false, &node); return node; } /* Similar to xml_get_node_by_path(), but creates the element and any requisite * ancestor elements as needed. If successful, 0 is returned and *node_ret is * set to a pointer to the resulting element. If unsuccessful, an error code is * returned and *node_ret is set to NULL. */ static int xml_ensure_node_by_path(xmlNode *root, const xmlChar *path, xmlNode **node_ret) { return do_xml_path_walk(root, path, true, node_ret); } static u64 xml_get_number_by_path(xmlNode *root, const xmlChar *path) { return node_get_number(xml_get_node_by_path(root, path), 10); } static u64 xml_get_timestamp_by_path(xmlNode *root, const xmlChar *path) { return node_get_timestamp(xml_get_node_by_path(root, path)); } static const xmlChar * xml_get_text_by_path(xmlNode *root, const xmlChar *path) { return node_get_text(xml_get_node_by_path(root, path)); } static const tchar * xml_get_ttext_by_path(struct wim_xml_info *info, xmlNode *root, const xmlChar *path) { return node_get_ttext(info, xml_get_node_by_path(root, path)); } /* Creates/replaces (if ttext is not NULL and not empty) or removes (if ttext is * NULL or empty) an element containing text. */ static int xml_set_ttext_by_path(xmlNode *root, const xmlChar *path, const tchar *ttext) { int ret; xmlNode *node; if (ttext && *ttext) { /* Create or replace */ ret = xml_ensure_node_by_path(root, path, &node); if (ret) return ret; return node_set_ttext(node, ttext); } else { /* Remove */ node = xml_get_node_by_path(root, path); if (node) unlink_and_free_tree(node); return 0; } } /* Unlink and return the node which represents the INDEX attribute of the * specified IMAGE element. */ static xmlAttr * unlink_index_attribute(xmlNode *image_node) { xmlAttr *attr = xmlHasProp(image_node, "INDEX"); xmlUnlinkNode((xmlNode *)attr); return attr; } /* Compute the total uncompressed size of the streams of the specified inode. */ static u64 inode_sum_stream_sizes(const struct wim_inode *inode, const struct blob_table *blob_table) { u64 total_size = 0; for (unsigned i = 0; i < inode->i_num_streams; i++) { const struct blob_descriptor *blob; blob = stream_blob(&inode->i_streams[i], blob_table); if (blob) total_size += blob->size; } return total_size; } static int append_image_node(struct wim_xml_info *info, xmlNode *image_node) { char buf[32]; xmlNode **images; /* Limit exceeded? */ if (unlikely(info->image_count >= MAX_IMAGES)) return WIMLIB_ERR_IMAGE_COUNT; /* Add the INDEX attribute. */ sprintf(buf, "%d", info->image_count + 1); if (!xmlNewProp(image_node, "INDEX", buf)) return WIMLIB_ERR_NOMEM; /* Append the IMAGE element to the 'images' array. */ images = REALLOC(info->images, (info->image_count + 1) * sizeof(info->images[0])); if (unlikely(!images)) return WIMLIB_ERR_NOMEM; info->images = images; images[info->image_count++] = image_node; /* Add the IMAGE element to the document. */ xmlAddChild(info->root, image_node); return 0; } /*----------------------------------------------------------------------------* * Functions for internal library use * *----------------------------------------------------------------------------*/ /* Allocate an empty 'struct wim_xml_info', containing no images. */ struct wim_xml_info * xml_new_info_struct(void) { struct wim_xml_info *info; info = alloc_wim_xml_info(); if (!info) goto err; info->doc = xmlNewDoc("1.0"); if (!info->doc) goto err_free_info; info->root = xmlNewNode(NULL, "WIM"); if (!info->root) goto err_free_doc; xmlDocSetRootElement(info->doc, info->root); info->images = NULL; info->image_count = 0; return info; err_free_doc: xmlFreeDoc(info->doc); err_free_info: FREE(info); err: return NULL; } /* Free a 'struct wim_xml_info'. */ void xml_free_info_struct(struct wim_xml_info *info) { if (info) { xmlFreeDoc(info->doc); FREE(info->images); #if TCHAR_IS_UTF16LE for (size_t i = 0; i < info->num_strings; i++) FREE(info->strings[i]); #endif FREE(info); } } /* Retrieve the number of images for which there exist IMAGE elements in the XML * document. */ int xml_get_image_count(const struct wim_xml_info *info) { return info->image_count; } /* Retrieve the TOTALBYTES value for the WIM file, or 0 if this value is * unavailable. */ u64 xml_get_total_bytes(const struct wim_xml_info *info) { return xml_get_number_by_path(info->root, "TOTALBYTES"); } /* Retrieve the TOTALBYTES value for the specified image, or 0 if this value is * unavailable. */ u64 xml_get_image_total_bytes(const struct wim_xml_info *info, int image) { return xml_get_number_by_path(info->images[image - 1], "TOTALBYTES"); } /* Retrieve the HARDLINKBYTES value for the specified image, or 0 if this value * is unavailable. */ u64 xml_get_image_hard_link_bytes(const struct wim_xml_info *info, int image) { return xml_get_number_by_path(info->images[image - 1], "HARDLINKBYTES"); } /* Retrieve the WIMBOOT value for the specified image, or false if this value is * unavailable. */ bool xml_get_wimboot(const struct wim_xml_info *info, int image) { return xml_get_number_by_path(info->images[image - 1], "WIMBOOT"); } /* Retrieve the Windows build number for the specified image, or 0 if this * information is not available. */ u64 xml_get_windows_build_number(const struct wim_xml_info *info, int image) { return xml_get_number_by_path(info->images[image - 1], "WINDOWS/VERSION/BUILD"); } /* Set the WIMBOOT value for the specified image. */ int xml_set_wimboot(struct wim_xml_info *info, int image) { return xml_set_ttext_by_path(info->images[image - 1], "WIMBOOT", T("1")); } /* * Update the DIRCOUNT, FILECOUNT, TOTALBYTES, HARDLINKBYTES, and * LASTMODIFICATIONTIME elements for the specified WIM image. * * Note: since these stats are likely to be used for display purposes only, we * no longer attempt to duplicate WIMGAPI's weird bugs when calculating them. */ int xml_update_image_info(WIMStruct *wim, int image) { const struct wim_image_metadata *imd = wim->image_metadata[image - 1]; xmlNode *image_node = wim->xml_info->images[image - 1]; const struct wim_inode *inode; u64 dir_count = 0; u64 file_count = 0; u64 total_bytes = 0; u64 hard_link_bytes = 0; u64 size; xmlNode *dircount_node; xmlNode *filecount_node; xmlNode *totalbytes_node; xmlNode *hardlinkbytes_node; xmlNode *lastmodificationtime_node; image_for_each_inode(inode, imd) { if (inode_is_directory(inode)) dir_count += inode->i_nlink; else file_count += inode->i_nlink; size = inode_sum_stream_sizes(inode, wim->blob_table); total_bytes += size * inode->i_nlink; hard_link_bytes += size * (inode->i_nlink - 1); } dircount_node = new_element_with_u64(NULL, "DIRCOUNT", dir_count); filecount_node = new_element_with_u64(NULL, "FILECOUNT", file_count); totalbytes_node = new_element_with_u64(NULL, "TOTALBYTES", total_bytes); hardlinkbytes_node = new_element_with_u64(NULL, "HARDLINKBYTES", hard_link_bytes); lastmodificationtime_node = new_element_with_timestamp(NULL, "LASTMODIFICATIONTIME", now_as_wim_timestamp()); if (unlikely(!dircount_node || !filecount_node || !totalbytes_node || !hardlinkbytes_node || !lastmodificationtime_node)) { xmlFreeNode(dircount_node); xmlFreeNode(filecount_node); xmlFreeNode(totalbytes_node); xmlFreeNode(hardlinkbytes_node); xmlFreeNode(lastmodificationtime_node); return WIMLIB_ERR_NOMEM; } node_replace_child_element(image_node, dircount_node); node_replace_child_element(image_node, filecount_node); node_replace_child_element(image_node, totalbytes_node); node_replace_child_element(image_node, hardlinkbytes_node); node_replace_child_element(image_node, lastmodificationtime_node); return 0; } /* Add an image to the XML information. */ int xml_add_image(struct wim_xml_info *info, const tchar *name) { const u64 now = now_as_wim_timestamp(); xmlNode *image_node; int ret; ret = WIMLIB_ERR_NOMEM; image_node = xmlNewNode(NULL, "IMAGE"); if (!image_node) goto err; if (name && *name) { ret = new_element_with_ttext(image_node, "NAME", name, NULL); if (ret) goto err; } ret = WIMLIB_ERR_NOMEM; if (!new_element_with_u64(image_node, "DIRCOUNT", 0)) goto err; if (!new_element_with_u64(image_node, "FILECOUNT", 0)) goto err; if (!new_element_with_u64(image_node, "TOTALBYTES", 0)) goto err; if (!new_element_with_u64(image_node, "HARDLINKBYTES", 0)) goto err; if (!new_element_with_timestamp(image_node, "CREATIONTIME", now)) goto err; if (!new_element_with_timestamp(image_node, "LASTMODIFICATIONTIME", now)) goto err; ret = append_image_node(info, image_node); if (ret) goto err; return 0; err: xmlFreeNode(image_node); return ret; } /* * Make a copy of the XML information for the image with index @src_image in the * @src_info XML document and append it to the @dest_info XML document. * * In the process, change the image's name and description to the values * specified by @dest_image_name and @dest_image_description. Either or both * may be NULL, which indicates that the corresponding element will not be * included in the destination image. */ int xml_export_image(const struct wim_xml_info *src_info, int src_image, struct wim_xml_info *dest_info, const tchar *dest_image_name, const tchar *dest_image_description, bool wimboot) { xmlNode *dest_node; int ret; ret = WIMLIB_ERR_NOMEM; dest_node = xmlDocCopyNode(src_info->images[src_image - 1], dest_info->doc, 1); if (!dest_node) goto err; ret = xml_set_ttext_by_path(dest_node, "NAME", dest_image_name); if (ret) goto err; ret = xml_set_ttext_by_path(dest_node, "DESCRIPTION", dest_image_description); if (ret) goto err; if (wimboot) { ret = xml_set_ttext_by_path(dest_node, "WIMBOOT", T("1")); if (ret) goto err; } xmlFreeProp(unlink_index_attribute(dest_node)); ret = append_image_node(dest_info, dest_node); if (ret) goto err; return 0; err: xmlFreeNode(dest_node); return ret; } /* Remove the specified image from the XML document. */ void xml_delete_image(struct wim_xml_info *info, int image) { xmlNode *next_image; xmlAttr *index_attr, *next_index_attr; /* Free the IMAGE element for the deleted image. Then, shift all * higher-indexed IMAGE elements down by 1, in the process re-assigning * their INDEX attributes. */ next_image = info->images[image - 1]; next_index_attr = unlink_index_attribute(next_image); unlink_and_free_tree(next_image); while (image < info->image_count) { index_attr = next_index_attr; next_image = info->images[image]; next_index_attr = unlink_index_attribute(next_image); xmlAddChild(next_image, (xmlNode *)index_attr); info->images[image - 1] = next_image; image++; } xmlFreeProp(next_index_attr); info->image_count--; } /* Architecture constants are from w64 mingw winnt.h */ #define PROCESSOR_ARCHITECTURE_INTEL 0 #define PROCESSOR_ARCHITECTURE_MIPS 1 #define PROCESSOR_ARCHITECTURE_ALPHA 2 #define PROCESSOR_ARCHITECTURE_PPC 3 #define PROCESSOR_ARCHITECTURE_SHX 4 #define PROCESSOR_ARCHITECTURE_ARM 5 #define PROCESSOR_ARCHITECTURE_IA64 6 #define PROCESSOR_ARCHITECTURE_ALPHA64 7 #define PROCESSOR_ARCHITECTURE_MSIL 8 #define PROCESSOR_ARCHITECTURE_AMD64 9 #define PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 10 #define PROCESSOR_ARCHITECTURE_ARM64 12 static const tchar * describe_arch(u64 arch) { static const tchar * const descriptions[] = { [PROCESSOR_ARCHITECTURE_INTEL] = T("x86"), [PROCESSOR_ARCHITECTURE_MIPS] = T("MIPS"), [PROCESSOR_ARCHITECTURE_ARM] = T("ARM"), [PROCESSOR_ARCHITECTURE_IA64] = T("ia64"), [PROCESSOR_ARCHITECTURE_AMD64] = T("x86_64"), [PROCESSOR_ARCHITECTURE_ARM64] = T("ARM64"), }; if (arch < ARRAY_LEN(descriptions) && descriptions[arch] != NULL) return descriptions[arch]; return T("unknown"); } /* Print information from the WINDOWS element, if present. */ static void print_windows_info(struct wim_xml_info *info, xmlNode *image_node) { xmlNode *windows_node; xmlNode *langs_node; xmlNode *version_node; const tchar *text; windows_node = xml_get_node_by_path(image_node, "WINDOWS"); if (!windows_node) return; tprintf(T("Architecture: %"TS"\n"), describe_arch(xml_get_number_by_path(windows_node, "ARCH"))); text = xml_get_ttext_by_path(info, windows_node, "PRODUCTNAME"); if (text) tprintf(T("Product Name: %"TS"\n"), text); text = xml_get_ttext_by_path(info, windows_node, "EDITIONID"); if (text) tprintf(T("Edition ID: %"TS"\n"), text); text = xml_get_ttext_by_path(info, windows_node, "INSTALLATIONTYPE"); if (text) tprintf(T("Installation Type: %"TS"\n"), text); text = xml_get_ttext_by_path(info, windows_node, "HAL"); if (text) tprintf(T("HAL: %"TS"\n"), text); text = xml_get_ttext_by_path(info, windows_node, "PRODUCTTYPE"); if (text) tprintf(T("Product Type: %"TS"\n"), text); text = xml_get_ttext_by_path(info, windows_node, "PRODUCTSUITE"); if (text) tprintf(T("Product Suite: %"TS"\n"), text); langs_node = xml_get_node_by_path(windows_node, "LANGUAGES"); if (langs_node) { xmlNode *lang_node; tprintf(T("Languages: ")); node_for_each_child(langs_node, lang_node) { if (!node_is_element(lang_node, "LANGUAGE")) continue; text = node_get_ttext(info, lang_node); if (!text) continue; tprintf(T("%"TS" "), text); } tputchar(T('\n')); text = xml_get_ttext_by_path(info, langs_node, "DEFAULT"); if (text) tprintf(T("Default Language: %"TS"\n"), text); } text = xml_get_ttext_by_path(info, windows_node, "SYSTEMROOT"); if (text) tprintf(T("System Root: %"TS"\n"), text); version_node = xml_get_node_by_path(windows_node, "VERSION"); if (version_node) { tprintf(T("Major Version: %"PRIu64"\n"), xml_get_number_by_path(version_node, "MAJOR")); tprintf(T("Minor Version: %"PRIu64"\n"), xml_get_number_by_path(version_node, "MINOR")); tprintf(T("Build: %"PRIu64"\n"), xml_get_number_by_path(version_node, "BUILD")); tprintf(T("Service Pack Build: %"PRIu64"\n"), xml_get_number_by_path(version_node, "SPBUILD")); tprintf(T("Service Pack Level: %"PRIu64"\n"), xml_get_number_by_path(version_node, "SPLEVEL")); } } /* Prints information about the specified image. */ void xml_print_image_info(struct wim_xml_info *info, int image) { xmlNode * const image_node = info->images[image - 1]; const tchar *text; tchar timebuf[64]; tprintf(T("Index: %d\n"), image); /* Always print the Name and Description, even if the corresponding XML * elements are not present. */ text = xml_get_ttext_by_path(info, image_node, "NAME"); tprintf(T("Name: %"TS"\n"), text ? text : T("")); text = xml_get_ttext_by_path(info, image_node, "DESCRIPTION"); tprintf(T("Description: %"TS"\n"), text ? text : T("")); text = xml_get_ttext_by_path(info, image_node, "DISPLAYNAME"); if (text) tprintf(T("Display Name: %"TS"\n"), text); text = xml_get_ttext_by_path(info, image_node, "DISPLAYDESCRIPTION"); if (text) tprintf(T("Display Description: %"TS"\n"), text); tprintf(T("Directory Count: %"PRIu64"\n"), xml_get_number_by_path(image_node, "DIRCOUNT")); tprintf(T("File Count: %"PRIu64"\n"), xml_get_number_by_path(image_node, "FILECOUNT")); tprintf(T("Total Bytes: %"PRIu64"\n"), xml_get_number_by_path(image_node, "TOTALBYTES")); tprintf(T("Hard Link Bytes: %"PRIu64"\n"), xml_get_number_by_path(image_node, "HARDLINKBYTES")); wim_timestamp_to_str(xml_get_timestamp_by_path(image_node, "CREATIONTIME"), timebuf, ARRAY_LEN(timebuf)); tprintf(T("Creation Time: %"TS"\n"), timebuf); wim_timestamp_to_str(xml_get_timestamp_by_path(image_node, "LASTMODIFICATIONTIME"), timebuf, ARRAY_LEN(timebuf)); tprintf(T("Last Modification Time: %"TS"\n"), timebuf); print_windows_info(info, image_node); text = xml_get_ttext_by_path(info, image_node, "FLAGS"); if (text) tprintf(T("Flags: %"TS"\n"), text); tprintf(T("WIMBoot compatible: %"TS"\n"), xml_get_number_by_path(image_node, "WIMBOOT") ? T("yes") : T("no")); tputchar('\n'); } /*----------------------------------------------------------------------------* * Reading and writing the XML data * *----------------------------------------------------------------------------*/ static int image_node_get_index(xmlNode *node) { u64 v = node_get_number((const xmlNode *)xmlHasProp(node, "INDEX"), 10); return min(v, INT_MAX); } /* Prepare the 'images' array from the XML document tree. */ static int setup_images(struct wim_xml_info *info, xmlNode *root) { xmlNode *child; int index; int max_index = 0; int ret; info->images = NULL; info->image_count = 0; node_for_each_child(root, child) { if (!node_is_element(child, "IMAGE")) continue; index = image_node_get_index(child); if (unlikely(index < 1 || info->image_count >= MAX_IMAGES)) goto err_indices; max_index = max(max_index, index); info->image_count++; } if (unlikely(max_index != info->image_count)) goto err_indices; ret = WIMLIB_ERR_NOMEM; info->images = CALLOC(info->image_count, sizeof(info->images[0])); if (unlikely(!info->images)) goto err; node_for_each_child(root, child) { if (!node_is_element(child, "IMAGE")) continue; index = image_node_get_index(child); if (unlikely(info->images[index - 1])) goto err_indices; info->images[index - 1] = child; } return 0; err_indices: ERROR("The WIM file's XML document does not contain exactly one IMAGE " "element per image!"); ret = WIMLIB_ERR_XML; err: FREE(info->images); return ret; } /* Reads the XML data from a WIM file. */ int read_wim_xml_data(WIMStruct *wim) { struct wim_xml_info *info; void *buf; size_t bufsize; xmlDoc *doc; xmlNode *root; int ret; /* Allocate the 'struct wim_xml_info'. */ ret = WIMLIB_ERR_NOMEM; info = alloc_wim_xml_info(); if (!info) goto err; /* Read the raw UTF-16LE bytes. */ ret = wimlib_get_xml_data(wim, &buf, &bufsize); if (ret) goto err_free_info; /* Parse the document with libxml2, creating the document tree. */ doc = xmlReadMemory(buf, bufsize, NULL, "UTF-16LE", XML_PARSE_NONET); FREE(buf); buf = NULL; if (!doc) { ERROR("Unable to parse the WIM file's XML document!"); ret = WIMLIB_ERR_XML; goto err_free_info; } /* Verify the root element. */ root = xmlDocGetRootElement(doc); if (!node_is_element(root, "WIM")) { ERROR("The WIM file's XML document has an unexpected format!"); ret = WIMLIB_ERR_XML; goto err_free_doc; } /* Verify the WIM file is not encrypted. */ if (xml_get_node_by_path(root, "ESD/ENCRYPTED")) { ret = WIMLIB_ERR_WIM_IS_ENCRYPTED; goto err_free_doc; } /* Validate the image elements and set up the images[] array. */ ret = setup_images(info, root); if (ret) goto err_free_doc; /* Save the document and return. */ info->doc = doc; info->root = root; wim->xml_info = info; return 0; err_free_doc: xmlFreeDoc(doc); err_free_info: FREE(info); err: return ret; } /* Swap the INDEX attributes of two IMAGE elements. */ static void swap_index_attributes(xmlNode *image_node_1, xmlNode *image_node_2) { xmlAttr *attr_1, *attr_2; if (image_node_1 != image_node_2) { attr_1 = unlink_index_attribute(image_node_1); attr_2 = unlink_index_attribute(image_node_2); xmlAddChild(image_node_1, (xmlNode *)attr_2); xmlAddChild(image_node_2, (xmlNode *)attr_1); } } static int prepare_document_for_write(struct wim_xml_info *info, int image, u64 total_bytes, xmlNode **orig_totalbytes_node_ret) { xmlNode *totalbytes_node = NULL; /* Allocate the new TOTALBYTES element if needed. */ if (total_bytes != WIM_TOTALBYTES_USE_EXISTING && total_bytes != WIM_TOTALBYTES_OMIT) { totalbytes_node = new_element_with_u64(NULL, "TOTALBYTES", total_bytes); if (!totalbytes_node) return WIMLIB_ERR_NOMEM; } /* Adjust the IMAGE elements if needed. */ if (image != WIMLIB_ALL_IMAGES) { /* We're writing a single image only. Temporarily unlink all * other IMAGE elements from the document. */ for (int i = 0; i < info->image_count; i++) if (i + 1 != image) xmlUnlinkNode(info->images[i]); /* Temporarily set the INDEX attribute of the needed IMAGE * element to 1. */ swap_index_attributes(info->images[0], info->images[image - 1]); } /* Adjust (add, change, or remove) the TOTALBYTES element if needed. */ *orig_totalbytes_node_ret = NULL; if (total_bytes != WIM_TOTALBYTES_USE_EXISTING) { /* Unlink the previous TOTALBYTES element, if any. */ *orig_totalbytes_node_ret = xml_get_node_by_path(info->root, "TOTALBYTES"); if (*orig_totalbytes_node_ret) xmlUnlinkNode(*orig_totalbytes_node_ret); /* Link in the new TOTALBYTES element, if any. */ if (totalbytes_node) xmlAddChild(info->root, totalbytes_node); } return 0; } static void restore_document_after_write(struct wim_xml_info *info, int image, xmlNode *orig_totalbytes_node) { /* Restore the IMAGE elements if needed. */ if (image != WIMLIB_ALL_IMAGES) { /* We wrote a single image only. Re-link all other IMAGE * elements to the document. */ for (int i = 0; i < info->image_count; i++) if (i + 1 != image) xmlAddChild(info->root, info->images[i]); /* Restore the original INDEX attributes. */ swap_index_attributes(info->images[0], info->images[image - 1]); } /* Restore the original TOTALBYTES element if needed. */ if (orig_totalbytes_node) node_replace_child_element(info->root, orig_totalbytes_node); } /* * Writes the XML data to a WIM file. * * 'image' specifies the image(s) to include in the XML data. Normally it is * WIMLIB_ALL_IMAGES, but it can also be a 1-based image index. * * 'total_bytes' is the number to use in the top-level TOTALBYTES element, or * WIM_TOTALBYTES_USE_EXISTING to use the existing value from the XML document * (if any), or WIM_TOTALBYTES_OMIT to omit the TOTALBYTES element entirely. */ int write_wim_xml_data(WIMStruct *wim, int image, u64 total_bytes, struct wim_reshdr *out_reshdr, int write_resource_flags) { struct wim_xml_info *info = wim->xml_info; long ret; long ret2; xmlBuffer *buffer; xmlNode *orig_totalbytes_node; xmlSaveCtxt *save_ctx; /* Make any needed temporary changes to the document. */ ret = prepare_document_for_write(info, image, total_bytes, &orig_totalbytes_node); if (ret) goto out; /* Create an in-memory buffer to hold the encoded document. */ ret = WIMLIB_ERR_NOMEM; buffer = xmlBufferCreate(); if (!buffer) goto out_restore_document; /* Encode the document in UTF-16LE, with a byte order mark, and with no * XML declaration. Some other WIM software requires all of these * characteristics. */ ret = WIMLIB_ERR_NOMEM; if (xmlBufferCat(buffer, "\xff\xfe")) goto out_free_buffer; save_ctx = xmlSaveToBuffer(buffer, "UTF-16LE", XML_SAVE_NO_DECL); if (!save_ctx) goto out_free_buffer; ret = xmlSaveDoc(save_ctx, info->doc); ret2 = xmlSaveClose(save_ctx); if (ret < 0 || ret2 < 0) { ERROR("Unable to serialize the WIM file's XML document!"); ret = WIMLIB_ERR_NOMEM; goto out_free_buffer; } /* Write the XML data uncompressed. Although wimlib can handle * compressed XML data, some other WIM software cannot. */ ret = write_wim_resource_from_buffer(xmlBufferContent(buffer), xmlBufferLength(buffer), true, &wim->out_fd, WIMLIB_COMPRESSION_TYPE_NONE, 0, out_reshdr, NULL, write_resource_flags); out_free_buffer: xmlBufferFree(buffer); out_restore_document: /* Revert any temporary changes we made to the document. */ restore_document_after_write(info, image, orig_totalbytes_node); out: return ret; } /*----------------------------------------------------------------------------* * Global setup functions * *----------------------------------------------------------------------------*/ void xml_global_init(void) { xmlInitParser(); } void xml_global_cleanup(void) { xmlCleanupParser(); } void xml_set_memory_allocator(void *(*malloc_func)(size_t), void (*free_func)(void *), void *(*realloc_func)(void *, size_t)) { xmlMemSetup(free_func, malloc_func, realloc_func, wimlib_strdup); } /*----------------------------------------------------------------------------* * Library API functions * *----------------------------------------------------------------------------*/ WIMLIBAPI int wimlib_get_xml_data(WIMStruct *wim, void **buf_ret, size_t *bufsize_ret) { const struct wim_reshdr *xml_reshdr; if (wim->filename == NULL && filedes_is_seekable(&wim->in_fd)) return WIMLIB_ERR_NO_FILENAME; if (buf_ret == NULL || bufsize_ret == NULL) return WIMLIB_ERR_INVALID_PARAM; xml_reshdr = &wim->hdr.xml_data_reshdr; *bufsize_ret = xml_reshdr->uncompressed_size; return wim_reshdr_to_data(xml_reshdr, wim, buf_ret); } WIMLIBAPI int wimlib_extract_xml_data(WIMStruct *wim, FILE *fp) { int ret; void *buf; size_t bufsize; ret = wimlib_get_xml_data(wim, &buf, &bufsize); if (ret) return ret; if (fwrite(buf, 1, bufsize, fp) != bufsize) { ERROR_WITH_ERRNO("Failed to extract XML data"); ret = WIMLIB_ERR_WRITE; } FREE(buf); return ret; } static bool image_name_in_use(const WIMStruct *wim, const tchar *name, int excluded_image) { const struct wim_xml_info *info = wim->xml_info; const xmlChar *name_utf8; bool found = false; /* Any number of images can have "no name". */ if (!name || !*name) return false; /* Check for images that have the specified name. */ if (tstr_get_utf8(name, &name_utf8)) return false; for (int i = 0; i < info->image_count && !found; i++) { if (i + 1 == excluded_image) continue; found = xmlStrEqual(name_utf8, xml_get_text_by_path( info->images[i], "NAME")); } tstr_put_utf8(name_utf8); return found; } WIMLIBAPI bool wimlib_image_name_in_use(const WIMStruct *wim, const tchar *name) { return image_name_in_use(wim, name, WIMLIB_NO_IMAGE); } WIMLIBAPI const tchar * wimlib_get_image_name(const WIMStruct *wim, int image) { const struct wim_xml_info *info = wim->xml_info; const tchar *name; if (image < 1 || image > info->image_count) return NULL; name = wimlib_get_image_property(wim, image, T("NAME")); return name ? name : T(""); } WIMLIBAPI const tchar * wimlib_get_image_description(const WIMStruct *wim, int image) { return wimlib_get_image_property(wim, image, T("DESCRIPTION")); } WIMLIBAPI const tchar * wimlib_get_image_property(const WIMStruct *wim, int image, const tchar *property_name) { const xmlChar *name; const tchar *value; struct wim_xml_info *info = wim->xml_info; if (!property_name || !*property_name) return NULL; if (image < 1 || image > info->image_count) return NULL; if (tstr_get_utf8(property_name, &name)) return NULL; value = xml_get_ttext_by_path(info, info->images[image - 1], name); tstr_put_utf8(name); return value; } WIMLIBAPI int wimlib_set_image_name(WIMStruct *wim, int image, const tchar *name) { return wimlib_set_image_property(wim, image, T("NAME"), name); } WIMLIBAPI int wimlib_set_image_descripton(WIMStruct *wim, int image, const tchar *description) { return wimlib_set_image_property(wim, image, T("DESCRIPTION"), description); } WIMLIBAPI int wimlib_set_image_flags(WIMStruct *wim, int image, const tchar *flags) { return wimlib_set_image_property(wim, image, T("FLAGS"), flags); } WIMLIBAPI int wimlib_set_image_property(WIMStruct *wim, int image, const tchar *property_name, const tchar *property_value) { const xmlChar *name; struct wim_xml_info *info = wim->xml_info; int ret; if (!property_name || !*property_name) return WIMLIB_ERR_INVALID_PARAM; if (image < 1 || image > info->image_count) return WIMLIB_ERR_INVALID_IMAGE; if (!tstrcmp(property_name, T("NAME")) && image_name_in_use(wim, property_value, image)) return WIMLIB_ERR_IMAGE_NAME_COLLISION; ret = tstr_get_utf8(property_name, &name); if (ret) return ret; ret = xml_set_ttext_by_path(info->images[image - 1], name, property_value); tstr_put_utf8(name); return ret; } wimlib-1.13.1/src/resource.c0000644000175000017500000012521413160354225012571 00000000000000/* * resource.c * * Code for reading blobs and resources, including compressed WIM resources. */ /* * Copyright (C) 2012, 2013, 2015 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include "wimlib/alloca.h" #include "wimlib/assert.h" #include "wimlib/bitops.h" #include "wimlib/blob_table.h" #include "wimlib/endianness.h" #include "wimlib/error.h" #include "wimlib/file_io.h" #include "wimlib/ntfs_3g.h" #include "wimlib/resource.h" #include "wimlib/sha1.h" #include "wimlib/wim.h" #include "wimlib/win32.h" /* * Compressed WIM resources * * A compressed resource in a WIM consists of a sequence of chunks. Each chunk * decompresses to the same size except possibly for the last, which * decompresses to the remaining size. Chunks that did not compress to less * than their original size are stored uncompressed. * * We support three variations on this resource format, independently of the * compression type and chunk size which can vary as well: * * - Original resource format: immediately before the compressed chunks, the * "chunk table" provides the offset, in bytes relative to the end of the * chunk table, of the start of each compressed chunk, except for the first * chunk which is omitted as it always has an offset of 0. Chunk table * entries are 32-bit for resources < 4 GiB uncompressed and 64-bit for * resources >= 4 GiB uncompressed. * * - Solid resource format (distinguished by the use of WIM_RESHDR_FLAG_SOLID * instead of WIM_RESHDR_FLAG_COMPRESSED): similar to the original format, but * the resource begins with a 16-byte header which specifies the uncompressed * size of the resource, the compression type, and the chunk size. (In the * original format, these values were instead determined from outside the * resource itself, from the blob table and the WIM file header.) In addition, * in this format the entries in the chunk table contain compressed chunk * sizes rather than offsets. As a consequence of this, the chunk table * entries are always 32-bit and there is an entry for chunk 0. * * - Pipable resource format (wimlib extension; all resources in a pipable WIM * have this format): similar to the original format, but the chunk table is * at the end of the resource rather than the beginning, and each compressed * chunk is prefixed with its compressed size as a 32-bit integer. This * format allows a resource to be written without rewinding. */ struct data_range { u64 offset; u64 size; }; /* * Read data from a compressed WIM resource. * * @rdesc * Description of the compressed WIM resource to read from. * @ranges * Nonoverlapping, nonempty ranges of the uncompressed resource data to * read, sorted by increasing offset. * @num_ranges * Number of ranges in @ranges; must be at least 1. * @cb * Structure which provides the consume_chunk callback into which to feed * the data being read. Each call provides the next chunk of the requested * data, uncompressed. Each chunk will be nonempty and will not cross * range boundaries but otherwise will be of unspecified size. * * Possible return values: * * WIMLIB_ERR_SUCCESS (0) * WIMLIB_ERR_READ (errno set) * WIMLIB_ERR_UNEXPECTED_END_OF_FILE (errno set to EINVAL) * WIMLIB_ERR_NOMEM (errno set to ENOMEM) * WIMLIB_ERR_DECOMPRESSION (errno set to EINVAL) * WIMLIB_ERR_INVALID_CHUNK_SIZE (errno set to EINVAL) * * or other error code returned by the callback function. */ static int read_compressed_wim_resource(const struct wim_resource_descriptor * const rdesc, const struct data_range * const ranges, const size_t num_ranges, const struct consume_chunk_callback *cb) { int ret; u64 *chunk_offsets = NULL; u8 *ubuf = NULL; void *cbuf = NULL; bool chunk_offsets_malloced = false; bool ubuf_malloced = false; bool cbuf_malloced = false; struct wimlib_decompressor *decompressor = NULL; /* Sanity checks */ wimlib_assert(num_ranges != 0); for (size_t i = 0; i < num_ranges; i++) { wimlib_assert(ranges[i].offset + ranges[i].size > ranges[i].offset && ranges[i].offset + ranges[i].size <= rdesc->uncompressed_size); } for (size_t i = 0; i < num_ranges - 1; i++) wimlib_assert(ranges[i].offset + ranges[i].size <= ranges[i + 1].offset); /* Get the offsets of the first and last bytes of the read. */ const u64 first_offset = ranges[0].offset; const u64 last_offset = ranges[num_ranges - 1].offset + ranges[num_ranges - 1].size - 1; /* Get the file descriptor for the WIM. */ struct filedes * const in_fd = &rdesc->wim->in_fd; /* Determine if we're reading a pipable resource from a pipe or not. */ const bool is_pipe_read = (rdesc->is_pipable && !filedes_is_seekable(in_fd)); /* Determine if the chunk table is in an alternate format. */ const bool alt_chunk_table = (rdesc->flags & WIM_RESHDR_FLAG_SOLID) && !is_pipe_read; /* Get the maximum size of uncompressed chunks in this resource, which * we require be a power of 2. */ u64 cur_read_offset = rdesc->offset_in_wim; int ctype = rdesc->compression_type; u32 chunk_size = rdesc->chunk_size; if (alt_chunk_table) { /* Alternate chunk table format. Its header specifies the chunk * size and compression format. Note: it could be read here; * however, the relevant data was already loaded into @rdesc by * read_blob_table(). */ cur_read_offset += sizeof(struct alt_chunk_table_header_disk); } if (unlikely(!is_power_of_2(chunk_size))) { ERROR("Invalid compressed resource: " "expected power-of-2 chunk size (got %"PRIu32")", chunk_size); ret = WIMLIB_ERR_INVALID_CHUNK_SIZE; errno = EINVAL; goto out_cleanup; } /* Get valid decompressor. */ if (likely(ctype == rdesc->wim->decompressor_ctype && chunk_size == rdesc->wim->decompressor_max_block_size)) { /* Cached decompressor. */ decompressor = rdesc->wim->decompressor; rdesc->wim->decompressor_ctype = WIMLIB_COMPRESSION_TYPE_NONE; rdesc->wim->decompressor = NULL; } else { ret = wimlib_create_decompressor(ctype, chunk_size, &decompressor); if (unlikely(ret)) { if (ret != WIMLIB_ERR_NOMEM) errno = EINVAL; goto out_cleanup; } } const u32 chunk_order = bsr32(chunk_size); /* Calculate the total number of chunks the resource is divided into. */ const u64 num_chunks = (rdesc->uncompressed_size + chunk_size - 1) >> chunk_order; /* Calculate the 0-based indices of the first and last chunks containing * data that needs to be passed to the callback. */ const u64 first_needed_chunk = first_offset >> chunk_order; const u64 last_needed_chunk = last_offset >> chunk_order; /* Calculate the 0-based index of the first chunk that actually needs to * be read. This is normally first_needed_chunk, but for pipe reads we * must always start from the 0th chunk. */ const u64 read_start_chunk = (is_pipe_read ? 0 : first_needed_chunk); /* Calculate the number of chunk offsets that are needed for the chunks * being read. */ const u64 num_needed_chunk_offsets = last_needed_chunk - read_start_chunk + 1 + (last_needed_chunk < num_chunks - 1); /* Calculate the number of entries in the chunk table. Normally, it's * one less than the number of chunks, since the first chunk has no * entry. But in the alternate chunk table format, the chunk entries * contain chunk sizes, not offsets, and there is one per chunk. */ const u64 num_chunk_entries = (alt_chunk_table ? num_chunks : num_chunks - 1); /* Set the size of each chunk table entry based on the resource's * uncompressed size. */ const u64 chunk_entry_size = get_chunk_entry_size(rdesc->uncompressed_size, alt_chunk_table); /* Calculate the size of the chunk table in bytes. */ const u64 chunk_table_size = num_chunk_entries * chunk_entry_size; /* Calculate the size of the chunk table in bytes, including the header * in the case of the alternate chunk table format. */ const u64 chunk_table_full_size = (alt_chunk_table) ? chunk_table_size + sizeof(struct alt_chunk_table_header_disk) : chunk_table_size; if (!is_pipe_read) { /* Read the needed chunk table entries into memory and use them * to initialize the chunk_offsets array. */ u64 first_chunk_entry_to_read; u64 num_chunk_entries_to_read; if (alt_chunk_table) { /* The alternate chunk table contains chunk sizes, not * offsets, so we always must read all preceding entries * in order to determine offsets. */ first_chunk_entry_to_read = 0; num_chunk_entries_to_read = last_needed_chunk + 1; } else { num_chunk_entries_to_read = last_needed_chunk - read_start_chunk + 1; /* The first chunk has no explicit chunk table entry. */ if (read_start_chunk == 0) { num_chunk_entries_to_read--; first_chunk_entry_to_read = 0; } else { first_chunk_entry_to_read = read_start_chunk - 1; } /* Unless we're reading the final chunk of the resource, * we need the offset of the chunk following the last * needed chunk so that the compressed size of the last * needed chunk can be computed. */ if (last_needed_chunk < num_chunks - 1) num_chunk_entries_to_read++; } const u64 chunk_offsets_alloc_size = max(num_chunk_entries_to_read, num_needed_chunk_offsets) * sizeof(chunk_offsets[0]); if (unlikely((size_t)chunk_offsets_alloc_size != chunk_offsets_alloc_size)) { errno = ENOMEM; goto oom; } if (likely(chunk_offsets_alloc_size <= STACK_MAX)) { chunk_offsets = alloca(chunk_offsets_alloc_size); } else { chunk_offsets = MALLOC(chunk_offsets_alloc_size); if (unlikely(!chunk_offsets)) goto oom; chunk_offsets_malloced = true; } const size_t chunk_table_size_to_read = num_chunk_entries_to_read * chunk_entry_size; const u64 file_offset_of_needed_chunk_entries = cur_read_offset + (first_chunk_entry_to_read * chunk_entry_size) + (rdesc->is_pipable ? (rdesc->size_in_wim - chunk_table_size) : 0); void * const chunk_table_data = (u8*)chunk_offsets + chunk_offsets_alloc_size - chunk_table_size_to_read; ret = full_pread(in_fd, chunk_table_data, chunk_table_size_to_read, file_offset_of_needed_chunk_entries); if (unlikely(ret)) goto read_error; /* Now fill in chunk_offsets from the entries we have read in * chunk_tab_data. We break aliasing rules here to avoid having * to allocate yet another array. */ typedef le64 _may_alias_attribute aliased_le64_t; typedef le32 _may_alias_attribute aliased_le32_t; u64 * chunk_offsets_p = chunk_offsets; if (alt_chunk_table) { u64 cur_offset = 0; aliased_le32_t *raw_entries = chunk_table_data; for (size_t i = 0; i < num_chunk_entries_to_read; i++) { u32 entry = le32_to_cpu(raw_entries[i]); if (i >= read_start_chunk) *chunk_offsets_p++ = cur_offset; cur_offset += entry; } if (last_needed_chunk < num_chunks - 1) *chunk_offsets_p = cur_offset; } else { if (read_start_chunk == 0) *chunk_offsets_p++ = 0; if (chunk_entry_size == 4) { aliased_le32_t *raw_entries = chunk_table_data; for (size_t i = 0; i < num_chunk_entries_to_read; i++) *chunk_offsets_p++ = le32_to_cpu(raw_entries[i]); } else { aliased_le64_t *raw_entries = chunk_table_data; for (size_t i = 0; i < num_chunk_entries_to_read; i++) *chunk_offsets_p++ = le64_to_cpu(raw_entries[i]); } } /* Set offset to beginning of first chunk to read. */ cur_read_offset += chunk_offsets[0]; if (rdesc->is_pipable) cur_read_offset += read_start_chunk * sizeof(struct pwm_chunk_hdr); else cur_read_offset += chunk_table_size; } /* Allocate buffer for holding the uncompressed data of each chunk. */ if (chunk_size <= STACK_MAX) { ubuf = alloca(chunk_size); } else { ubuf = MALLOC(chunk_size); if (unlikely(!ubuf)) goto oom; ubuf_malloced = true; } /* Allocate a temporary buffer for reading compressed chunks, each of * which can be at most @chunk_size - 1 bytes. This excludes compressed * chunks that are a full @chunk_size bytes, which are actually stored * uncompressed. */ if (chunk_size - 1 <= STACK_MAX) { cbuf = alloca(chunk_size - 1); } else { cbuf = MALLOC(chunk_size - 1); if (unlikely(!cbuf)) goto oom; cbuf_malloced = true; } /* Set current data range. */ const struct data_range *cur_range = ranges; const struct data_range * const end_range = &ranges[num_ranges]; u64 cur_range_pos = cur_range->offset; u64 cur_range_end = cur_range->offset + cur_range->size; /* Read and process each needed chunk. */ for (u64 i = read_start_chunk; i <= last_needed_chunk; i++) { /* Calculate uncompressed size of next chunk. */ u32 chunk_usize; if ((i == num_chunks - 1) && (rdesc->uncompressed_size & (chunk_size - 1))) chunk_usize = (rdesc->uncompressed_size & (chunk_size - 1)); else chunk_usize = chunk_size; /* Calculate compressed size of next chunk. */ u32 chunk_csize; if (is_pipe_read) { struct pwm_chunk_hdr chunk_hdr; ret = full_pread(in_fd, &chunk_hdr, sizeof(chunk_hdr), cur_read_offset); if (unlikely(ret)) goto read_error; chunk_csize = le32_to_cpu(chunk_hdr.compressed_size); } else { if (i == num_chunks - 1) { chunk_csize = rdesc->size_in_wim - chunk_table_full_size - chunk_offsets[i - read_start_chunk]; if (rdesc->is_pipable) chunk_csize -= num_chunks * sizeof(struct pwm_chunk_hdr); } else { chunk_csize = chunk_offsets[i + 1 - read_start_chunk] - chunk_offsets[i - read_start_chunk]; } } if (unlikely(chunk_csize == 0 || chunk_csize > chunk_usize)) { ERROR("Invalid chunk size in compressed resource!"); errno = EINVAL; ret = WIMLIB_ERR_DECOMPRESSION; goto out_cleanup; } if (rdesc->is_pipable) cur_read_offset += sizeof(struct pwm_chunk_hdr); /* Offsets in the uncompressed resource at which this chunk * starts and ends. */ const u64 chunk_start_offset = i << chunk_order; const u64 chunk_end_offset = chunk_start_offset + chunk_usize; if (chunk_end_offset <= cur_range_pos) { /* The next range does not require data in this chunk, * so skip it. */ cur_read_offset += chunk_csize; if (is_pipe_read) { u8 dummy; ret = full_pread(in_fd, &dummy, 1, cur_read_offset - 1); if (unlikely(ret)) goto read_error; } } else { /* Read the chunk and feed data to the callback * function. */ u8 *read_buf; if (chunk_csize == chunk_usize) read_buf = ubuf; else read_buf = cbuf; ret = full_pread(in_fd, read_buf, chunk_csize, cur_read_offset); if (unlikely(ret)) goto read_error; if (read_buf == cbuf) { ret = wimlib_decompress(cbuf, chunk_csize, ubuf, chunk_usize, decompressor); if (unlikely(ret)) { ERROR("Failed to decompress data!"); ret = WIMLIB_ERR_DECOMPRESSION; errno = EINVAL; goto out_cleanup; } } cur_read_offset += chunk_csize; /* At least one range requires data in this chunk. */ do { size_t start, end, size; /* Calculate how many bytes of data should be * sent to the callback function, taking into * account that data sent to the callback * function must not overlap range boundaries. */ start = cur_range_pos - chunk_start_offset; end = min(cur_range_end, chunk_end_offset) - chunk_start_offset; size = end - start; ret = consume_chunk(cb, &ubuf[start], size); if (unlikely(ret)) goto out_cleanup; cur_range_pos += size; if (cur_range_pos == cur_range_end) { /* Advance to next range. */ if (++cur_range == end_range) { cur_range_pos = ~0ULL; } else { cur_range_pos = cur_range->offset; cur_range_end = cur_range->offset + cur_range->size; } } } while (cur_range_pos < chunk_end_offset); } } if (is_pipe_read && last_offset == rdesc->uncompressed_size - 1 && chunk_table_size) { u8 dummy; /* If reading a pipable resource from a pipe and the full data * was requested, skip the chunk table at the end so that the * file descriptor is fully clear of the resource after this * returns. */ cur_read_offset += chunk_table_size; ret = full_pread(in_fd, &dummy, 1, cur_read_offset - 1); if (unlikely(ret)) goto read_error; } ret = 0; out_cleanup: if (decompressor) { wimlib_free_decompressor(rdesc->wim->decompressor); rdesc->wim->decompressor = decompressor; rdesc->wim->decompressor_ctype = ctype; rdesc->wim->decompressor_max_block_size = chunk_size; } if (chunk_offsets_malloced) FREE(chunk_offsets); if (ubuf_malloced) FREE(ubuf); if (cbuf_malloced) FREE(cbuf); return ret; oom: ERROR("Out of memory while reading compressed WIM resource"); ret = WIMLIB_ERR_NOMEM; goto out_cleanup; read_error: ERROR_WITH_ERRNO("Error reading data from WIM file"); goto out_cleanup; } /* Read raw data from a file descriptor at the specified offset, feeding the * data in nonempty chunks into the specified callback function. */ static int read_raw_file_data(struct filedes *in_fd, u64 offset, u64 size, const struct consume_chunk_callback *cb, const tchar *filename) { u8 buf[BUFFER_SIZE]; size_t bytes_to_read; int ret; while (size) { bytes_to_read = min(sizeof(buf), size); ret = full_pread(in_fd, buf, bytes_to_read, offset); if (unlikely(ret)) goto read_error; ret = consume_chunk(cb, buf, bytes_to_read); if (unlikely(ret)) return ret; size -= bytes_to_read; offset += bytes_to_read; } return 0; read_error: if (!filename) { ERROR_WITH_ERRNO("Error reading data from WIM file"); } else if (ret == WIMLIB_ERR_UNEXPECTED_END_OF_FILE) { ERROR("\"%"TS"\": File was concurrently truncated", filename); ret = WIMLIB_ERR_CONCURRENT_MODIFICATION_DETECTED; } else { ERROR_WITH_ERRNO("\"%"TS"\": Error reading data", filename); } return ret; } /* A consume_chunk implementation which simply concatenates all chunks into an * in-memory buffer. */ static int bufferer_cb(const void *chunk, size_t size, void *_ctx) { void **buf_p = _ctx; *buf_p = mempcpy(*buf_p, chunk, size); return 0; } /* * Read @size bytes at @offset in the WIM resource described by @rdesc and feed * the data into the @cb callback function. * * @offset and @size are assumed to have already been validated against the * resource's uncompressed size. * * Returns 0 on success; or the first nonzero value returned by the callback * function; or a nonzero wimlib error code with errno set as well. */ static int read_partial_wim_resource(const struct wim_resource_descriptor *rdesc, const u64 offset, const u64 size, const struct consume_chunk_callback *cb) { if (rdesc->flags & (WIM_RESHDR_FLAG_COMPRESSED | WIM_RESHDR_FLAG_SOLID)) { /* Compressed resource */ if (unlikely(!size)) return 0; struct data_range range = { .offset = offset, .size = size, }; return read_compressed_wim_resource(rdesc, &range, 1, cb); } /* Uncompressed resource */ return read_raw_file_data(&rdesc->wim->in_fd, rdesc->offset_in_wim + offset, size, cb, NULL); } /* Read the specified range of uncompressed data from the specified blob, which * must be located in a WIM file, into the specified buffer. */ int read_partial_wim_blob_into_buf(const struct blob_descriptor *blob, u64 offset, size_t size, void *buf) { struct consume_chunk_callback cb = { .func = bufferer_cb, .ctx = &buf, }; return read_partial_wim_resource(blob->rdesc, blob->offset_in_res + offset, size, &cb); } static int noop_cb(const void *chunk, size_t size, void *_ctx) { return 0; } /* Skip over the data of the specified WIM resource. */ int skip_wim_resource(const struct wim_resource_descriptor *rdesc) { static const struct consume_chunk_callback cb = { .func = noop_cb, }; return read_partial_wim_resource(rdesc, 0, rdesc->uncompressed_size, &cb); } static int read_wim_blob_prefix(const struct blob_descriptor *blob, u64 size, const struct consume_chunk_callback *cb) { return read_partial_wim_resource(blob->rdesc, blob->offset_in_res, size, cb); } /* This function handles reading blob data that is located in an external file, * such as a file that has been added to the WIM image through execution of a * wimlib_add_command. * * This assumes the file can be accessed using the standard POSIX open(), * read(), and close(). On Windows this will not necessarily be the case (since * the file may need FILE_FLAG_BACKUP_SEMANTICS to be opened, or the file may be * encrypted), so Windows uses its own code for its equivalent case. */ static int read_file_on_disk_prefix(const struct blob_descriptor *blob, u64 size, const struct consume_chunk_callback *cb) { int ret; int raw_fd; struct filedes fd; raw_fd = topen(blob->file_on_disk, O_BINARY | O_RDONLY); if (unlikely(raw_fd < 0)) { ERROR_WITH_ERRNO("Can't open \"%"TS"\"", blob->file_on_disk); return WIMLIB_ERR_OPEN; } filedes_init(&fd, raw_fd); ret = read_raw_file_data(&fd, 0, size, cb, blob->file_on_disk); filedes_close(&fd); return ret; } #ifdef WITH_FUSE static int read_staging_file_prefix(const struct blob_descriptor *blob, u64 size, const struct consume_chunk_callback *cb) { int raw_fd; struct filedes fd; int ret; raw_fd = openat(blob->staging_dir_fd, blob->staging_file_name, O_RDONLY | O_NOFOLLOW); if (unlikely(raw_fd < 0)) { ERROR_WITH_ERRNO("Can't open staging file \"%s\"", blob->staging_file_name); return WIMLIB_ERR_OPEN; } filedes_init(&fd, raw_fd); ret = read_raw_file_data(&fd, 0, size, cb, blob->staging_file_name); filedes_close(&fd); return ret; } #endif /* This function handles the trivial case of reading blob data that is, in fact, * already located in an in-memory buffer. */ static int read_buffer_prefix(const struct blob_descriptor *blob, u64 size, const struct consume_chunk_callback *cb) { if (unlikely(!size)) return 0; return consume_chunk(cb, blob->attached_buffer, size); } typedef int (*read_blob_prefix_handler_t)(const struct blob_descriptor *blob, u64 size, const struct consume_chunk_callback *cb); /* * Read the first @size bytes from a generic "blob", which may be located in any * one of several locations, such as in a WIM resource (possibly compressed), in * an external file, or directly in an in-memory buffer. The blob data will be * fed to @cb in chunks that are nonempty but otherwise are of unspecified size. * * Returns 0 on success; nonzero on error. A nonzero value will be returned if * the blob data cannot be successfully read (for a number of different reasons, * depending on the blob location), or if @cb returned nonzero in which case * that error code will be returned. */ static int read_blob_prefix(const struct blob_descriptor *blob, u64 size, const struct consume_chunk_callback *cb) { static const read_blob_prefix_handler_t handlers[] = { [BLOB_IN_WIM] = read_wim_blob_prefix, [BLOB_IN_FILE_ON_DISK] = read_file_on_disk_prefix, [BLOB_IN_ATTACHED_BUFFER] = read_buffer_prefix, #ifdef WITH_FUSE [BLOB_IN_STAGING_FILE] = read_staging_file_prefix, #endif #ifdef WITH_NTFS_3G [BLOB_IN_NTFS_VOLUME] = read_ntfs_attribute_prefix, #endif #ifdef __WIN32__ [BLOB_IN_WINDOWS_FILE] = read_windows_file_prefix, #endif }; wimlib_assert(blob->blob_location < ARRAY_LEN(handlers) && handlers[blob->blob_location] != NULL); wimlib_assert(size <= blob->size); return handlers[blob->blob_location](blob, size, cb); } struct blob_chunk_ctx { const struct blob_descriptor *blob; const struct read_blob_callbacks *cbs; u64 offset; }; static int consume_blob_chunk(const void *chunk, size_t size, void *_ctx) { struct blob_chunk_ctx *ctx = _ctx; int ret; ret = call_continue_blob(ctx->blob, ctx->offset, chunk, size, ctx->cbs); ctx->offset += size; return ret; } /* Read the full data of the specified blob, passing the data into the specified * callbacks (all of which are optional). */ int read_blob_with_cbs(struct blob_descriptor *blob, const struct read_blob_callbacks *cbs) { int ret; struct blob_chunk_ctx ctx = { .blob = blob, .offset = 0, .cbs = cbs, }; struct consume_chunk_callback cb = { .func = consume_blob_chunk, .ctx = &ctx, }; ret = call_begin_blob(blob, cbs); if (unlikely(ret)) return ret; ret = read_blob_prefix(blob, blob->size, &cb); return call_end_blob(blob, ret, cbs); } /* Read the full uncompressed data of the specified blob into the specified * buffer, which must have space for at least blob->size bytes. The SHA-1 * message digest is *not* checked. */ int read_blob_into_buf(const struct blob_descriptor *blob, void *buf) { struct consume_chunk_callback cb = { .func = bufferer_cb, .ctx = &buf, }; return read_blob_prefix(blob, blob->size, &cb); } /* Retrieve the full uncompressed data of the specified blob. A buffer large * enough hold the data is allocated and returned in @buf_ret. The SHA-1 * message digest is *not* checked. */ int read_blob_into_alloc_buf(const struct blob_descriptor *blob, void **buf_ret) { int ret; void *buf; if (unlikely((size_t)blob->size != blob->size)) { ERROR("Can't read %"PRIu64" byte blob into memory", blob->size); return WIMLIB_ERR_NOMEM; } buf = MALLOC(blob->size); if (unlikely(!buf)) return WIMLIB_ERR_NOMEM; ret = read_blob_into_buf(blob, buf); if (unlikely(ret)) { FREE(buf); return ret; } *buf_ret = buf; return 0; } /* Retrieve the full uncompressed data of a WIM resource specified as a raw * `wim_reshdr' and the corresponding WIM file. A buffer large enough hold the * data is allocated and returned in @buf_ret. */ int wim_reshdr_to_data(const struct wim_reshdr *reshdr, WIMStruct *wim, void **buf_ret) { struct wim_resource_descriptor rdesc; struct blob_descriptor blob; wim_reshdr_to_desc_and_blob(reshdr, wim, &rdesc, &blob); return read_blob_into_alloc_buf(&blob, buf_ret); } /* Calculate the SHA-1 message digest of the uncompressed data of the specified * WIM resource. */ int wim_reshdr_to_hash(const struct wim_reshdr *reshdr, WIMStruct *wim, u8 hash[SHA1_HASH_SIZE]) { struct wim_resource_descriptor rdesc; struct blob_descriptor blob; int ret; wim_reshdr_to_desc_and_blob(reshdr, wim, &rdesc, &blob); blob.unhashed = 1; ret = sha1_blob(&blob); if (unlikely(ret)) return ret; copy_hash(hash, blob.hash); return 0; } struct blobifier_context { struct read_blob_callbacks cbs; struct blob_descriptor *cur_blob; struct blob_descriptor *next_blob; u64 cur_blob_offset; struct blob_descriptor *final_blob; size_t list_head_offset; }; static struct blob_descriptor * next_blob(struct blob_descriptor *blob, size_t list_head_offset) { struct list_head *cur; cur = (struct list_head*)((u8*)blob + list_head_offset); return (struct blob_descriptor*)((u8*)cur->next - list_head_offset); } /* * A consume_chunk implementation that translates raw resource data into blobs, * calling the begin_blob, continue_blob, and end_blob callbacks as appropriate. */ static int blobifier_cb(const void *chunk, size_t size, void *_ctx) { struct blobifier_context *ctx = _ctx; int ret; wimlib_assert(ctx->cur_blob != NULL); wimlib_assert(size <= ctx->cur_blob->size - ctx->cur_blob_offset); if (ctx->cur_blob_offset == 0) { /* Starting a new blob. */ ret = call_begin_blob(ctx->cur_blob, &ctx->cbs); if (ret) return ret; } ret = call_continue_blob(ctx->cur_blob, ctx->cur_blob_offset, chunk, size, &ctx->cbs); ctx->cur_blob_offset += size; if (ret) return ret; if (ctx->cur_blob_offset == ctx->cur_blob->size) { /* Finished reading all the data for a blob. */ ctx->cur_blob_offset = 0; ret = call_end_blob(ctx->cur_blob, 0, &ctx->cbs); if (ret) return ret; /* Advance to next blob. */ ctx->cur_blob = ctx->next_blob; if (ctx->cur_blob != NULL) { if (ctx->cur_blob != ctx->final_blob) ctx->next_blob = next_blob(ctx->cur_blob, ctx->list_head_offset); else ctx->next_blob = NULL; } } return 0; } struct hasher_context { SHA_CTX sha_ctx; int flags; struct read_blob_callbacks cbs; }; /* Callback for starting to read a blob while calculating its SHA-1 message * digest. */ static int hasher_begin_blob(struct blob_descriptor *blob, void *_ctx) { struct hasher_context *ctx = _ctx; sha1_init(&ctx->sha_ctx); return call_begin_blob(blob, &ctx->cbs); } /* * A continue_blob() implementation that continues calculating the SHA-1 message * digest of the blob being read, then optionally passes the data on to another * continue_blob() implementation. This allows checking the SHA-1 message * digest of a blob being extracted, for example. */ static int hasher_continue_blob(const struct blob_descriptor *blob, u64 offset, const void *chunk, size_t size, void *_ctx) { struct hasher_context *ctx = _ctx; sha1_update(&ctx->sha_ctx, chunk, size); return call_continue_blob(blob, offset, chunk, size, &ctx->cbs); } static int report_sha1_mismatch_error(const struct blob_descriptor *blob, const u8 actual_hash[SHA1_HASH_SIZE]) { tchar expected_hashstr[SHA1_HASH_SIZE * 2 + 1]; tchar actual_hashstr[SHA1_HASH_SIZE * 2 + 1]; wimlib_assert(blob->blob_location != BLOB_NONEXISTENT); wimlib_assert(blob->blob_location != BLOB_IN_ATTACHED_BUFFER); sprint_hash(blob->hash, expected_hashstr); sprint_hash(actual_hash, actual_hashstr); if (blob_is_in_file(blob)) { ERROR("A file was concurrently modified!\n" " Path: \"%"TS"\"\n" " Expected SHA-1: %"TS"\n" " Actual SHA-1: %"TS"\n", blob_file_path(blob), expected_hashstr, actual_hashstr); return WIMLIB_ERR_CONCURRENT_MODIFICATION_DETECTED; } else if (blob->blob_location == BLOB_IN_WIM) { const struct wim_resource_descriptor *rdesc = blob->rdesc; ERROR("A WIM resource is corrupted!\n" " WIM file: \"%"TS"\"\n" " Blob uncompressed size: %"PRIu64"\n" " Resource offset in WIM: %"PRIu64"\n" " Resource uncompressed size: %"PRIu64"\n" " Resource size in WIM: %"PRIu64"\n" " Resource flags: 0x%x%"TS"\n" " Resource compression type: %"TS"\n" " Resource compression chunk size: %"PRIu32"\n" " Expected SHA-1: %"TS"\n" " Actual SHA-1: %"TS"\n", rdesc->wim->filename, blob->size, rdesc->offset_in_wim, rdesc->uncompressed_size, rdesc->size_in_wim, (unsigned int)rdesc->flags, (rdesc->is_pipable ? T(", pipable") : T("")), wimlib_get_compression_type_string( rdesc->compression_type), rdesc->chunk_size, expected_hashstr, actual_hashstr); return WIMLIB_ERR_INVALID_RESOURCE_HASH; } else { ERROR("File data was concurrently modified!\n" " Location ID: %d\n" " Expected SHA-1: %"TS"\n" " Actual SHA-1: %"TS"\n", (int)blob->blob_location, expected_hashstr, actual_hashstr); return WIMLIB_ERR_CONCURRENT_MODIFICATION_DETECTED; } } /* Callback for finishing reading a blob while calculating its SHA-1 message * digest. */ static int hasher_end_blob(struct blob_descriptor *blob, int status, void *_ctx) { struct hasher_context *ctx = _ctx; u8 hash[SHA1_HASH_SIZE]; int ret; if (unlikely(status)) { /* Error occurred; the full blob may not have been read. */ ret = status; goto out_next_cb; } /* Retrieve the final SHA-1 message digest. */ sha1_final(hash, &ctx->sha_ctx); /* Set the SHA-1 message digest of the blob, or compare the calculated * value with stored value. */ if (blob->unhashed) { if (ctx->flags & COMPUTE_MISSING_BLOB_HASHES) copy_hash(blob->hash, hash); } else if ((ctx->flags & VERIFY_BLOB_HASHES) && unlikely(!hashes_equal(hash, blob->hash))) { ret = report_sha1_mismatch_error(blob, hash); goto out_next_cb; } ret = 0; out_next_cb: return call_end_blob(blob, ret, &ctx->cbs); } /* Read the full data of the specified blob, passing the data into the specified * callbacks (all of which are optional) and either checking or computing the * SHA-1 message digest of the blob. */ int read_blob_with_sha1(struct blob_descriptor *blob, const struct read_blob_callbacks *cbs) { struct hasher_context hasher_ctx = { .flags = VERIFY_BLOB_HASHES | COMPUTE_MISSING_BLOB_HASHES, .cbs = *cbs, }; struct read_blob_callbacks hasher_cbs = { .begin_blob = hasher_begin_blob, .continue_blob = hasher_continue_blob, .end_blob = hasher_end_blob, .ctx = &hasher_ctx, }; return read_blob_with_cbs(blob, &hasher_cbs); } static int read_blobs_in_solid_resource(struct blob_descriptor *first_blob, struct blob_descriptor *last_blob, size_t blob_count, size_t list_head_offset, const struct read_blob_callbacks *sink_cbs) { struct data_range *ranges; bool ranges_malloced; struct blob_descriptor *cur_blob; size_t i; int ret; u64 ranges_alloc_size; /* Setup data ranges array (one range per blob to read); this way * read_compressed_wim_resource() does not need to be aware of blobs. */ ranges_alloc_size = (u64)blob_count * sizeof(ranges[0]); if (unlikely((size_t)ranges_alloc_size != ranges_alloc_size)) goto oom; if (ranges_alloc_size <= STACK_MAX) { ranges = alloca(ranges_alloc_size); ranges_malloced = false; } else { ranges = MALLOC(ranges_alloc_size); if (unlikely(!ranges)) goto oom; ranges_malloced = true; } for (i = 0, cur_blob = first_blob; i < blob_count; i++, cur_blob = next_blob(cur_blob, list_head_offset)) { ranges[i].offset = cur_blob->offset_in_res; ranges[i].size = cur_blob->size; } struct blobifier_context blobifier_ctx = { .cbs = *sink_cbs, .cur_blob = first_blob, .next_blob = next_blob(first_blob, list_head_offset), .cur_blob_offset = 0, .final_blob = last_blob, .list_head_offset = list_head_offset, }; struct consume_chunk_callback cb = { .func = blobifier_cb, .ctx = &blobifier_ctx, }; ret = read_compressed_wim_resource(first_blob->rdesc, ranges, blob_count, &cb); if (ranges_malloced) FREE(ranges); if (unlikely(ret && blobifier_ctx.cur_blob_offset != 0)) { ret = call_end_blob(blobifier_ctx.cur_blob, ret, &blobifier_ctx.cbs); } return ret; oom: ERROR("Too many blobs in one resource!"); return WIMLIB_ERR_NOMEM; } /* * Read a list of blobs, each of which may be in any supported location (e.g. * in a WIM or in an external file). This function optimizes the case where * multiple blobs are combined into a single solid compressed WIM resource by * reading the blobs in sequential order, only decompressing the solid resource * one time. * * @blob_list * List of blobs to read. * @list_head_offset * Offset of the `struct list_head' within each `struct blob_descriptor' * that makes up the @blob_list. * @cbs * Callback functions to accept the blob data. * @flags * Bitwise OR of zero or more of the following flags: * * VERIFY_BLOB_HASHES: * For all blobs being read that have already had SHA-1 message * digests computed, calculate the SHA-1 message digest of the read * data and compare it with the previously computed value. If they * do not match, return WIMLIB_ERR_INVALID_RESOURCE_HASH. * * COMPUTE_MISSING_BLOB_HASHES * For all blobs being read that have not yet had their SHA-1 * message digests computed, calculate and save their SHA-1 message * digests. * * BLOB_LIST_ALREADY_SORTED * @blob_list is already sorted in sequential order for reading. * * The callback functions are allowed to delete the current blob from the list * if necessary. * * Returns 0 on success; a nonzero error code on failure. Failure can occur due * to an error reading the data or due to an error status being returned by any * of the callback functions. */ int read_blob_list(struct list_head *blob_list, size_t list_head_offset, const struct read_blob_callbacks *cbs, int flags) { int ret; struct list_head *cur, *next; struct blob_descriptor *blob; struct hasher_context *hasher_ctx; struct read_blob_callbacks *sink_cbs; if (!(flags & BLOB_LIST_ALREADY_SORTED)) { ret = sort_blob_list_by_sequential_order(blob_list, list_head_offset); if (ret) return ret; } if (flags & (VERIFY_BLOB_HASHES | COMPUTE_MISSING_BLOB_HASHES)) { hasher_ctx = alloca(sizeof(*hasher_ctx)); *hasher_ctx = (struct hasher_context) { .flags = flags, .cbs = *cbs, }; sink_cbs = alloca(sizeof(*sink_cbs)); *sink_cbs = (struct read_blob_callbacks) { .begin_blob = hasher_begin_blob, .continue_blob = hasher_continue_blob, .end_blob = hasher_end_blob, .ctx = hasher_ctx, }; } else { sink_cbs = (struct read_blob_callbacks *)cbs; } for (cur = blob_list->next, next = cur->next; cur != blob_list; cur = next, next = cur->next) { blob = (struct blob_descriptor*)((u8*)cur - list_head_offset); if (blob->blob_location == BLOB_IN_WIM && blob->size != blob->rdesc->uncompressed_size) { struct blob_descriptor *blob_next, *blob_last; struct list_head *next2; size_t blob_count; /* The next blob is a proper sub-sequence of a WIM * resource. See if there are other blobs in the same * resource that need to be read. Since * sort_blob_list_by_sequential_order() sorted the blobs * by offset in the WIM, this can be determined by * simply scanning forward in the list. */ blob_last = blob; blob_count = 1; for (next2 = next; next2 != blob_list && (blob_next = (struct blob_descriptor*) ((u8*)next2 - list_head_offset), blob_next->blob_location == BLOB_IN_WIM && blob_next->rdesc == blob->rdesc); next2 = next2->next) { blob_last = blob_next; blob_count++; } if (blob_count > 1) { /* Reading multiple blobs combined into a single * WIM resource. They are in the blob list, * sorted by offset; @blob specifies the first * blob in the resource that needs to be read * and @blob_last specifies the last blob in the * resource that needs to be read. */ next = next2; ret = read_blobs_in_solid_resource(blob, blob_last, blob_count, list_head_offset, sink_cbs); if (ret) return ret; continue; } } ret = read_blob_with_cbs(blob, sink_cbs); if (unlikely(ret && ret != BEGIN_BLOB_STATUS_SKIP_BLOB)) return ret; } return 0; } static int extract_chunk_to_fd(const void *chunk, size_t size, void *_fd) { struct filedes *fd = _fd; int ret = full_write(fd, chunk, size); if (unlikely(ret)) ERROR_WITH_ERRNO("Error writing to file descriptor"); return ret; } static int extract_blob_chunk_to_fd(const struct blob_descriptor *blob, u64 offset, const void *chunk, size_t size, void *_fd) { return extract_chunk_to_fd(chunk, size, _fd); } /* Extract the first @size bytes of the specified blob to the specified file * descriptor. This does *not* check the SHA-1 message digest. */ int extract_blob_prefix_to_fd(struct blob_descriptor *blob, u64 size, struct filedes *fd) { struct consume_chunk_callback cb = { .func = extract_chunk_to_fd, .ctx = fd, }; return read_blob_prefix(blob, size, &cb); } /* Extract the full uncompressed contents of the specified blob to the specified * file descriptor. This checks the SHA-1 message digest. */ int extract_blob_to_fd(struct blob_descriptor *blob, struct filedes *fd) { struct read_blob_callbacks cbs = { .continue_blob = extract_blob_chunk_to_fd, .ctx = fd, }; return read_blob_with_sha1(blob, &cbs); } /* Calculate the SHA-1 message digest of a blob and store it in @blob->hash. */ int sha1_blob(struct blob_descriptor *blob) { static const struct read_blob_callbacks cbs = { }; return read_blob_with_sha1(blob, &cbs); } /* * Convert a short WIM resource header to a stand-alone WIM resource descriptor. * * Note: for solid resources some fields still need to be overridden. */ void wim_reshdr_to_desc(const struct wim_reshdr *reshdr, WIMStruct *wim, struct wim_resource_descriptor *rdesc) { rdesc->wim = wim; rdesc->offset_in_wim = reshdr->offset_in_wim; rdesc->size_in_wim = reshdr->size_in_wim; rdesc->uncompressed_size = reshdr->uncompressed_size; INIT_LIST_HEAD(&rdesc->blob_list); rdesc->flags = reshdr->flags; rdesc->is_pipable = wim_is_pipable(wim); if (rdesc->flags & WIM_RESHDR_FLAG_COMPRESSED) { rdesc->compression_type = wim->compression_type; rdesc->chunk_size = wim->chunk_size; } else { rdesc->compression_type = WIMLIB_COMPRESSION_TYPE_NONE; rdesc->chunk_size = 0; } } /* * Convert the short WIM resource header @reshdr to a stand-alone WIM resource * descriptor @rdesc, then set @blob to consist of that entire resource. This * should only be used for non-solid resources! */ void wim_reshdr_to_desc_and_blob(const struct wim_reshdr *reshdr, WIMStruct *wim, struct wim_resource_descriptor *rdesc, struct blob_descriptor *blob) { wim_reshdr_to_desc(reshdr, wim, rdesc); blob->size = rdesc->uncompressed_size; blob_set_is_located_in_wim_resource(blob, rdesc, 0); } /* Import a WIM resource header from the on-disk format. */ void get_wim_reshdr(const struct wim_reshdr_disk *disk_reshdr, struct wim_reshdr *reshdr) { reshdr->offset_in_wim = le64_to_cpu(disk_reshdr->offset_in_wim); reshdr->size_in_wim = (((u64)disk_reshdr->size_in_wim[0] << 0) | ((u64)disk_reshdr->size_in_wim[1] << 8) | ((u64)disk_reshdr->size_in_wim[2] << 16) | ((u64)disk_reshdr->size_in_wim[3] << 24) | ((u64)disk_reshdr->size_in_wim[4] << 32) | ((u64)disk_reshdr->size_in_wim[5] << 40) | ((u64)disk_reshdr->size_in_wim[6] << 48)); reshdr->uncompressed_size = le64_to_cpu(disk_reshdr->uncompressed_size); reshdr->flags = disk_reshdr->flags; } /* Export a WIM resource header to the on-disk format. */ void put_wim_reshdr(const struct wim_reshdr *reshdr, struct wim_reshdr_disk *disk_reshdr) { disk_reshdr->size_in_wim[0] = reshdr->size_in_wim >> 0; disk_reshdr->size_in_wim[1] = reshdr->size_in_wim >> 8; disk_reshdr->size_in_wim[2] = reshdr->size_in_wim >> 16; disk_reshdr->size_in_wim[3] = reshdr->size_in_wim >> 24; disk_reshdr->size_in_wim[4] = reshdr->size_in_wim >> 32; disk_reshdr->size_in_wim[5] = reshdr->size_in_wim >> 40; disk_reshdr->size_in_wim[6] = reshdr->size_in_wim >> 48; disk_reshdr->flags = reshdr->flags; disk_reshdr->offset_in_wim = cpu_to_le64(reshdr->offset_in_wim); disk_reshdr->uncompressed_size = cpu_to_le64(reshdr->uncompressed_size); } wimlib-1.13.1/src/security.c0000644000175000017500000002457313160354225012617 00000000000000/* * security.c * * Read and write the per-WIM-image table of security descriptors. */ /* * Copyright (C) 2012, 2013, 2014 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib/assert.h" #include "wimlib/avl_tree.h" #include "wimlib/endianness.h" #include "wimlib/error.h" #include "wimlib/security.h" #include "wimlib/sha1.h" #include "wimlib/util.h" struct wim_security_data_disk { le32 total_length; le32 num_entries; le64 sizes[]; } _packed_attribute; struct wim_security_data * new_wim_security_data(void) { return CALLOC(1, sizeof(struct wim_security_data)); } /* * Reads the security data from the metadata resource of a WIM image. * * @buf * Buffer containing an uncompressed WIM metadata resource. * @buf_len * Length of the uncompressed metadata resource, in bytes. * @sd_ret * On success, a pointer to the resulting security data structure will be * returned here. * * Note: There is no `offset' argument because the security data is located at * the beginning of the metadata resource. * * Return values: * WIMLIB_ERR_SUCCESS (0) * WIMLIB_ERR_INVALID_METADATA_RESOURCE * WIMLIB_ERR_NOMEM */ int read_wim_security_data(const u8 *buf, size_t buf_len, struct wim_security_data **sd_ret) { struct wim_security_data *sd; int ret; u64 total_len; u64 sizes_size; u64 size_no_descriptors; const struct wim_security_data_disk *sd_disk; const u8 *p; if (buf_len < 8) return WIMLIB_ERR_INVALID_METADATA_RESOURCE; sd = new_wim_security_data(); if (!sd) goto out_of_memory; sd_disk = (const struct wim_security_data_disk *)buf; sd->total_length = ALIGN(le32_to_cpu(sd_disk->total_length), 8); sd->num_entries = le32_to_cpu(sd_disk->num_entries); /* Length field of 0 is a special case that really means length * of 8. */ if (sd->total_length == 0) sd->total_length = 8; /* The security_id field of each dentry is a signed 32-bit integer, so * the possible indices into the security descriptors table are 0 * through 0x7fffffff. Which means 0x80000000 security descriptors * maximum. Not like you should ever have anywhere close to that many * security descriptors! */ if (sd->num_entries > 0x80000000) goto out_invalid_sd; /* Verify the listed total length of the security data is big enough to * include the sizes array, verify that the file data is big enough to * include it as well, then allocate the array of sizes. * * Note: The total length of the security data must fit in a 32-bit * integer, even though each security descriptor size is a 64-bit * integer. This is stupid, and we need to be careful not to actually * let the security descriptor sizes be over 0xffffffff. */ if (sd->total_length > buf_len) goto out_invalid_sd; sizes_size = (u64)sd->num_entries * sizeof(u64); size_no_descriptors = 8 + sizes_size; if (size_no_descriptors > sd->total_length) goto out_invalid_sd; total_len = size_no_descriptors; /* Return immediately if no security descriptors. */ if (sd->num_entries == 0) goto out_descriptors_ready; /* Allocate a new buffer for the sizes array */ sd->sizes = MALLOC(sizes_size); if (!sd->sizes) goto out_of_memory; /* Copy the sizes array into the new buffer */ for (u32 i = 0; i < sd->num_entries; i++) { sd->sizes[i] = le64_to_cpu(sd_disk->sizes[i]); if (sd->sizes[i] > 0xffffffff) goto out_invalid_sd; } p = (const u8*)sd_disk + size_no_descriptors; /* Allocate the array of pointers to the security descriptors, then read * them into separate buffers. */ sd->descriptors = CALLOC(sd->num_entries, sizeof(sd->descriptors[0])); if (!sd->descriptors) goto out_of_memory; for (u32 i = 0; i < sd->num_entries; i++) { if (sd->sizes[i] == 0) continue; total_len += sd->sizes[i]; if (total_len > (u64)sd->total_length) goto out_invalid_sd; sd->descriptors[i] = memdup(p, sd->sizes[i]); if (!sd->descriptors[i]) goto out_of_memory; p += sd->sizes[i]; } out_descriptors_ready: if (ALIGN(total_len, 8) != sd->total_length) { WARNING("Stored WIM security data total length was " "%"PRIu32" bytes, but calculated %"PRIu32" bytes", sd->total_length, (u32)total_len); } *sd_ret = sd; ret = 0; goto out; out_invalid_sd: ERROR("WIM security data is invalid!"); ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE; goto out_free_sd; out_of_memory: ERROR("Out of memory while reading WIM security data!"); ret = WIMLIB_ERR_NOMEM; out_free_sd: free_wim_security_data(sd); out: return ret; } /* * Writes the security data for a WIM image to an in-memory buffer. */ u8 * write_wim_security_data(const struct wim_security_data * restrict sd, u8 * restrict p) { u8 *orig_p = p; struct wim_security_data_disk *sd_disk = (struct wim_security_data_disk*)p; u32 num_entries = sd->num_entries; sd_disk->total_length = cpu_to_le32(sd->total_length); sd_disk->num_entries = cpu_to_le32(num_entries); for (u32 i = 0; i < num_entries; i++) sd_disk->sizes[i] = cpu_to_le64(sd->sizes[i]); p = (u8*)&sd_disk->sizes[num_entries]; for (u32 i = 0; i < num_entries; i++) p = mempcpy(p, sd->descriptors[i], sd->sizes[i]); while ((uintptr_t)p & 7) *p++ = 0; wimlib_assert(p - orig_p == sd->total_length); return p; } void free_wim_security_data(struct wim_security_data *sd) { if (sd) { u8 **descriptors = sd->descriptors; u32 num_entries = sd->num_entries; if (descriptors) while (num_entries--) FREE(*descriptors++); FREE(sd->sizes); FREE(sd->descriptors); FREE(sd); } } struct sd_node { s32 security_id; u8 hash[SHA1_HASH_SIZE]; struct avl_tree_node index_node; }; #define SD_NODE(avl_node) \ avl_tree_entry(avl_node, struct sd_node, index_node) static void free_sd_tree(struct avl_tree_node *node) { if (node) { free_sd_tree(node->left); free_sd_tree(node->right); FREE(SD_NODE(node)); } } void rollback_new_security_descriptors(struct wim_sd_set *sd_set) { struct wim_security_data *sd = sd_set->sd; u8 **descriptors = sd->descriptors + sd_set->orig_num_entries; u32 num_entries = sd->num_entries - sd_set->orig_num_entries; while (num_entries--) FREE(*descriptors++); sd->num_entries = sd_set->orig_num_entries; } /* Frees a security descriptor index set. */ void destroy_sd_set(struct wim_sd_set *sd_set) { free_sd_tree(sd_set->root); } static int _avl_cmp_nodes_by_hash(const struct avl_tree_node *n1, const struct avl_tree_node *n2) { return hashes_cmp(SD_NODE(n1)->hash, SD_NODE(n2)->hash); } /* Inserts a new node into the security descriptor index tree. Returns true * if successful (not a duplicate). */ static bool insert_sd_node(struct wim_sd_set *set, struct sd_node *new) { return NULL == avl_tree_insert(&set->root, &new->index_node, _avl_cmp_nodes_by_hash); } /* Returns the index of the security descriptor having a SHA1 message digest of * @hash. If not found, return -1. */ static s32 lookup_sd(struct wim_sd_set *set, const u8 hash[SHA1_HASH_SIZE]) { struct avl_tree_node *res; struct sd_node dummy; copy_hash(dummy.hash, hash); res = avl_tree_lookup_node(set->root, &dummy.index_node, _avl_cmp_nodes_by_hash); if (!res) return -1; return SD_NODE(res)->security_id; } /* * Adds a security descriptor to the indexed security descriptor set as well as * the corresponding `struct wim_security_data', and returns the new security * ID; or, if there is an existing security descriptor that is the same, return * the security ID for it. If a new security descriptor cannot be allocated, * return -1. */ s32 sd_set_add_sd(struct wim_sd_set *sd_set, const char *descriptor, size_t size) { u8 hash[SHA1_HASH_SIZE]; s32 security_id; struct sd_node *new; u8 **descriptors; u64 *sizes; u8 *descr_copy; struct wim_security_data *sd; bool bret; sha1_buffer(descriptor, size, hash); security_id = lookup_sd(sd_set, hash); if (security_id >= 0) /* Identical descriptor already exists */ goto out; /* Need to add a new security descriptor */ security_id = -1; new = MALLOC(sizeof(*new)); if (!new) goto out; descr_copy = memdup(descriptor, size); if (!descr_copy) goto out_free_node; sd = sd_set->sd; new->security_id = sd->num_entries; copy_hash(new->hash, hash); /* There typically are only a few dozen security descriptors in a * directory tree, so expanding the array of security descriptors by * only 1 extra space each time should not be a problem. */ descriptors = REALLOC(sd->descriptors, (sd->num_entries + 1) * sizeof(sd->descriptors[0])); if (!descriptors) goto out_free_descr; sd->descriptors = descriptors; sizes = REALLOC(sd->sizes, (sd->num_entries + 1) * sizeof(sd->sizes[0])); if (!sizes) goto out_free_descr; sd->sizes = sizes; sd->descriptors[sd->num_entries] = descr_copy; sd->sizes[sd->num_entries] = size; sd->num_entries++; bret = insert_sd_node(sd_set, new); wimlib_assert(bret); security_id = new->security_id; goto out; out_free_descr: FREE(descr_copy); out_free_node: FREE(new); out: return security_id; } /* Initialize a `struct sd_set' mapping from SHA1 message digests of security * descriptors to indices into the security descriptors table of the WIM image * (security IDs). */ int init_sd_set(struct wim_sd_set *sd_set, struct wim_security_data *sd) { int ret; sd_set->sd = sd; sd_set->root = NULL; /* Remember the original number of security descriptors so that newly * added ones can be rolled back if needed. */ sd_set->orig_num_entries = sd->num_entries; for (u32 i = 0; i < sd->num_entries; i++) { struct sd_node *new; new = MALLOC(sizeof(struct sd_node)); if (!new) { ret = WIMLIB_ERR_NOMEM; goto out_destroy_sd_set; } sha1_buffer(sd->descriptors[i], sd->sizes[i], new->hash); new->security_id = i; if (!insert_sd_node(sd_set, new)) FREE(new); /* Ignore duplicate security descriptor */ } ret = 0; goto out; out_destroy_sd_set: destroy_sd_set(sd_set); out: return ret; } wimlib-1.13.1/src/xpress_compress.c0000644000175000017500000010340013160354225014172 00000000000000/* * xpress_compress.c * * A compressor for the XPRESS compression format (Huffman variant). */ /* * Copyright (C) 2012, 2013, 2014 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif /* * The maximum buffer size, in bytes, that can be compressed. An XPRESS * compressor instance must be created with a 'max_bufsize' less than or equal * to this value. */ #define XPRESS_MAX_BUFSIZE 65536 /* * Define to 1 to enable the near-optimal parsing algorithm at high compression * levels. The near-optimal parsing algorithm produces a compression ratio * significantly better than the greedy and lazy algorithms. However, it is * much slower. */ #define SUPPORT_NEAR_OPTIMAL_PARSING 1 /* * The lowest compression level at which near-optimal parsing is enabled. */ #define MIN_LEVEL_FOR_NEAR_OPTIMAL 60 /* * Matchfinder definitions. For XPRESS, only a 16-bit matchfinder is needed. */ #define mf_pos_t u16 #define MF_SUFFIX /* * Note: although XPRESS can potentially use a sliding window, it isn't well * suited for large buffers of data because there is no way to reset the Huffman * code. Therefore, we only allow buffers in which there is no restriction on * match offsets (no sliding window). This simplifies the code and allows some * optimizations. */ #include "wimlib/bitops.h" #include "wimlib/compress_common.h" #include "wimlib/compressor_ops.h" #include "wimlib/endianness.h" #include "wimlib/error.h" #include "wimlib/hc_matchfinder.h" #include "wimlib/unaligned.h" #include "wimlib/util.h" #include "wimlib/xpress_constants.h" #if SUPPORT_NEAR_OPTIMAL_PARSING /* * CACHE_RESERVE_PER_POS is the number of lz_match structures to reserve in the * match cache for each byte position. This value should be high enough so that * virtually the time, all matches found in the input buffer can fit in the * match cache. However, fallback behavior on cache overflow is still required. */ #define CACHE_RESERVE_PER_POS 8 /* * We use a binary-tree based matchfinder for optimal parsing because it can * find more matches in the same number of steps compared to hash-chain based * matchfinders. In addition, since we need to find matches at almost every * position, there isn't much penalty for keeping the sequences sorted in the * binary trees. */ #include "wimlib/bt_matchfinder.h" struct xpress_optimum_node; #endif /* SUPPORT_NEAR_OPTIMAL_PARSING */ struct xpress_item; /* The main XPRESS compressor structure */ struct xpress_compressor { /* Pointer to the compress() implementation chosen at allocation time */ size_t (*impl)(struct xpress_compressor *, const void *, size_t, void *, size_t); /* Symbol frequency counters for the Huffman code */ u32 freqs[XPRESS_NUM_SYMBOLS]; /* The Huffman codewords and their lengths */ u32 codewords[XPRESS_NUM_SYMBOLS]; u8 lens[XPRESS_NUM_SYMBOLS]; /* The "nice" match length: if a match of this length is found, then * choose it immediately without further consideration. */ unsigned nice_match_length; /* The maximum search depth: consider at most this many potential * matches at each position. */ unsigned max_search_depth; union { /* Data for greedy or lazy parsing */ struct { struct xpress_item *chosen_items; struct hc_matchfinder hc_mf; /* hc_mf must be last! */ }; #if SUPPORT_NEAR_OPTIMAL_PARSING /* Data for near-optimal parsing */ struct { struct xpress_optimum_node *optimum_nodes; struct lz_match *match_cache; struct lz_match *cache_overflow_mark; unsigned num_optim_passes; u32 costs[XPRESS_NUM_SYMBOLS]; struct bt_matchfinder bt_mf; /* bt_mf must be last! */ }; #endif }; }; #if SUPPORT_NEAR_OPTIMAL_PARSING /* * This structure represents a byte position in the input buffer and a node in * the graph of possible match/literal choices. * * Logically, each incoming edge to this node is labeled with a literal or a * match that can be taken to reach this position from an earlier position; and * each outgoing edge from this node is labeled with a literal or a match that * can be taken to advance from this position to a later position. * * But these "edges" are actually stored elsewhere (in 'match_cache'). Here we * associate with each node just two pieces of information: * * 'cost_to_end' is the minimum cost to reach the end of the buffer from * this position. * * 'item' represents the literal or match that must be chosen from here to * reach the end of the buffer with the minimum cost. Equivalently, this * can be interpreted as the label of the outgoing edge on the minimum cost * path to the "end of buffer" node from this node. */ struct xpress_optimum_node { u32 cost_to_end; /* * Notes on the match/literal representation used here: * * The low bits of 'item' are the length: 1 if the item is a * literal, or the match length if the item is a match. * * The high bits of 'item' are the actual literal byte if the item * is a literal, or the match offset if the item is a match. */ #define OPTIMUM_OFFSET_SHIFT 16 #define OPTIMUM_LEN_MASK (((u32)1 << OPTIMUM_OFFSET_SHIFT) - 1) u32 item; }; #endif /* SUPPORT_NEAR_OPTIMAL_PARSING */ /* An intermediate representation of an XPRESS match or literal */ struct xpress_item { /* * Bits 0 - 8: Symbol * Bits 9 - 24: Length - XPRESS_MIN_MATCH_LEN * Bits 25 - 28: Number of extra offset bits * Bits 29+ : Extra offset bits * * Unfortunately, gcc generates worse code if we use real bitfields here. */ u64 data; }; /* * Structure to keep track of the current state of sending compressed data to * the output buffer. * * The XPRESS bitstream is encoded as a sequence of little endian 16-bit coding * units interwoven with literal bytes. */ struct xpress_output_bitstream { /* Bits that haven't yet been written to the output buffer. */ u32 bitbuf; /* Number of bits currently held in @bitbuf. */ u32 bitcount; /* Pointer to the start of the output buffer. */ u8 *start; /* Pointer to the location in the output buffer at which to write the * next 16 bits. */ u8 *next_bits; /* Pointer to the location in the output buffer at which to write the * next 16 bits, after @next_bits. */ u8 *next_bits2; /* Pointer to the location in the output buffer at which to write the * next literal byte. */ u8 *next_byte; /* Pointer to the end of the output buffer. */ u8 *end; }; /* Reset the symbol frequencies for the XPRESS Huffman code. */ static void xpress_reset_symbol_frequencies(struct xpress_compressor *c) { memset(c->freqs, 0, sizeof(c->freqs)); } /* * Make the Huffman code for XPRESS. * * Input: c->freqs * Output: c->lens and c->codewords */ static void xpress_make_huffman_code(struct xpress_compressor *c) { make_canonical_huffman_code(XPRESS_NUM_SYMBOLS, XPRESS_MAX_CODEWORD_LEN, c->freqs, c->lens, c->codewords); } /* * Initialize the output bitstream. * * @os * The output bitstream structure to initialize. * @buffer * The output buffer. * @size * Size of @buffer, in bytes. Must be at least 4. */ static void xpress_init_output(struct xpress_output_bitstream *os, void *buffer, size_t size) { os->bitbuf = 0; os->bitcount = 0; os->start = buffer; os->next_bits = os->start; os->next_bits2 = os->start + 2; os->next_byte = os->start + 4; os->end = os->start + size; } /* * Write some bits to the output bitstream. * * The bits are given by the low-order @num_bits bits of @bits. Higher-order * bits in @bits cannot be set. At most 16 bits can be written at once. * * If the output buffer space is exhausted, then the bits will be ignored, and * xpress_flush_output() will return 0 when it gets called. */ static forceinline void xpress_write_bits(struct xpress_output_bitstream *os, const u32 bits, const unsigned num_bits) { /* This code is optimized for XPRESS, which never needs to write more * than 16 bits at once. */ os->bitcount += num_bits; os->bitbuf = (os->bitbuf << num_bits) | bits; if (os->bitcount > 16) { os->bitcount -= 16; if (os->end - os->next_byte >= 2) { put_unaligned_le16(os->bitbuf >> os->bitcount, os->next_bits); os->next_bits = os->next_bits2; os->next_bits2 = os->next_byte; os->next_byte += 2; } } } /* * Interweave a literal byte into the output bitstream. */ static forceinline void xpress_write_byte(struct xpress_output_bitstream *os, u8 byte) { if (os->next_byte < os->end) *os->next_byte++ = byte; } /* * Interweave two literal bytes into the output bitstream. */ static forceinline void xpress_write_u16(struct xpress_output_bitstream *os, u16 v) { if (os->end - os->next_byte >= 2) { put_unaligned_le16(v, os->next_byte); os->next_byte += 2; } } /* * Flush the last coding unit to the output buffer if needed. Return the total * number of bytes written to the output buffer, or 0 if an overflow occurred. */ static size_t xpress_flush_output(struct xpress_output_bitstream *os) { if (os->end - os->next_byte < 2) return 0; put_unaligned_le16(os->bitbuf << (16 - os->bitcount), os->next_bits); put_unaligned_le16(0, os->next_bits2); return os->next_byte - os->start; } static forceinline void xpress_write_extra_length_bytes(struct xpress_output_bitstream *os, unsigned adjusted_len) { /* If length >= 18, output one extra length byte. * If length >= 273, output three (total) extra length bytes. */ if (adjusted_len >= 0xF) { u8 byte1 = min(adjusted_len - 0xF, 0xFF); xpress_write_byte(os, byte1); if (byte1 == 0xFF) xpress_write_u16(os, adjusted_len); } } /* Output a match or literal. */ static forceinline void xpress_write_item(struct xpress_item item, struct xpress_output_bitstream *os, const u32 codewords[], const u8 lens[]) { u64 data = item.data; unsigned symbol = data & 0x1FF; xpress_write_bits(os, codewords[symbol], lens[symbol]); if (symbol >= XPRESS_NUM_CHARS) { /* Match, not a literal */ xpress_write_extra_length_bytes(os, (data >> 9) & 0xFFFF); xpress_write_bits(os, data >> 29, (data >> 25) & 0xF); } } /* Output a sequence of XPRESS matches and literals. */ static void xpress_write_items(struct xpress_output_bitstream *os, const struct xpress_item items[], size_t num_items, const u32 codewords[], const u8 lens[]) { for (size_t i = 0; i < num_items; i++) xpress_write_item(items[i], os, codewords, lens); } #if SUPPORT_NEAR_OPTIMAL_PARSING /* * Follow the minimum cost path in the graph of possible match/literal choices * and write out the matches/literals using the specified Huffman code. * * Note: this is slightly duplicated with xpress_write_items(). However, we * don't want to waste time translating between intermediate match/literal * representations. */ static void xpress_write_item_list(struct xpress_output_bitstream *os, struct xpress_optimum_node *optimum_nodes, size_t count, const u32 codewords[], const u8 lens[]) { struct xpress_optimum_node *cur_node = optimum_nodes; struct xpress_optimum_node *end_node = optimum_nodes + count; do { unsigned length = cur_node->item & OPTIMUM_LEN_MASK; unsigned offset = cur_node->item >> OPTIMUM_OFFSET_SHIFT; if (length == 1) { /* Literal */ unsigned literal = offset; xpress_write_bits(os, codewords[literal], lens[literal]); } else { /* Match */ unsigned adjusted_len; unsigned log2_offset; unsigned len_hdr; unsigned sym; adjusted_len = length - XPRESS_MIN_MATCH_LEN; log2_offset = bsr32(offset); len_hdr = min(0xF, adjusted_len); sym = XPRESS_NUM_CHARS + ((log2_offset << 4) | len_hdr); xpress_write_bits(os, codewords[sym], lens[sym]); xpress_write_extra_length_bytes(os, adjusted_len); xpress_write_bits(os, offset - (1U << log2_offset), log2_offset); } cur_node += length; } while (cur_node != end_node); } #endif /* SUPPORT_NEAR_OPTIMAL_PARSING */ /* * Output the XPRESS-compressed data, given the sequence of match/literal * "items" that was chosen to represent the input data. * * If @near_optimal is %false, then the items are taken from the array * c->chosen_items[0...count]. * * If @near_optimal is %true, then the items are taken from the minimum cost * path stored in c->optimum_nodes[0...count]. */ static size_t xpress_write(struct xpress_compressor *c, void *out, size_t out_nbytes_avail, size_t count, bool near_optimal) { u8 *cptr; struct xpress_output_bitstream os; size_t out_size; /* Account for the end-of-data symbol and make the Huffman code. */ c->freqs[XPRESS_END_OF_DATA]++; xpress_make_huffman_code(c); /* Output the Huffman code as a series of 512 4-bit lengths. */ cptr = out; for (unsigned i = 0; i < XPRESS_NUM_SYMBOLS; i += 2) *cptr++ = (c->lens[i + 1] << 4) | c->lens[i]; xpress_init_output(&os, cptr, out_nbytes_avail - XPRESS_NUM_SYMBOLS / 2); /* Output the Huffman-encoded items. */ #if SUPPORT_NEAR_OPTIMAL_PARSING if (near_optimal) { xpress_write_item_list(&os, c->optimum_nodes, count, c->codewords, c->lens); } else #endif { xpress_write_items(&os, c->chosen_items, count, c->codewords, c->lens); } /* Write the end-of-data symbol (needed for MS compatibility) */ xpress_write_bits(&os, c->codewords[XPRESS_END_OF_DATA], c->lens[XPRESS_END_OF_DATA]); /* Flush any pending data. Then return the compressed size if the * compressed data fit in the output buffer, or 0 if it did not. */ out_size = xpress_flush_output(&os); if (out_size == 0) return 0; return out_size + XPRESS_NUM_SYMBOLS / 2; } /* Tally the Huffman symbol for a literal and return the intermediate * representation of that literal. */ static forceinline struct xpress_item xpress_record_literal(struct xpress_compressor *c, unsigned literal) { c->freqs[literal]++; return (struct xpress_item) { .data = literal, }; } /* Tally the Huffman symbol for a match and return the intermediate * representation of that match. */ static forceinline struct xpress_item xpress_record_match(struct xpress_compressor *c, unsigned length, unsigned offset) { unsigned adjusted_len = length - XPRESS_MIN_MATCH_LEN; unsigned len_hdr = min(adjusted_len, 0xF); unsigned log2_offset = bsr32(offset); unsigned sym = XPRESS_NUM_CHARS + ((log2_offset << 4) | len_hdr); c->freqs[sym]++; return (struct xpress_item) { .data = (u64)sym | ((u64)adjusted_len << 9) | ((u64)log2_offset << 25) | ((u64)(offset ^ (1U << log2_offset)) << 29), }; } /* * This is the "greedy" XPRESS compressor. It always chooses the longest match. * (Exception: as a heuristic, we pass up length 3 matches that have large * offsets.) */ static size_t xpress_compress_greedy(struct xpress_compressor * restrict c, const void * restrict in, size_t in_nbytes, void * restrict out, size_t out_nbytes_avail) { const u8 * const in_begin = in; const u8 * in_next = in_begin; const u8 * const in_end = in_begin + in_nbytes; struct xpress_item *next_chosen_item = c->chosen_items; unsigned len_3_too_far; u32 next_hashes[2] = {}; if (in_nbytes <= 8192) len_3_too_far = 2048; else len_3_too_far = 4096; hc_matchfinder_init(&c->hc_mf); do { unsigned length; unsigned offset; length = hc_matchfinder_longest_match(&c->hc_mf, in_begin, in_next - in_begin, XPRESS_MIN_MATCH_LEN - 1, in_end - in_next, min(in_end - in_next, c->nice_match_length), c->max_search_depth, next_hashes, &offset); if (length >= XPRESS_MIN_MATCH_LEN && !(length == XPRESS_MIN_MATCH_LEN && offset >= len_3_too_far)) { /* Match found */ *next_chosen_item++ = xpress_record_match(c, length, offset); in_next += 1; hc_matchfinder_skip_positions(&c->hc_mf, in_begin, in_next - in_begin, in_end - in_begin, length - 1, next_hashes); in_next += length - 1; } else { /* No match found */ *next_chosen_item++ = xpress_record_literal(c, *in_next); in_next += 1; } } while (in_next != in_end); return xpress_write(c, out, out_nbytes_avail, next_chosen_item - c->chosen_items, false); } /* * This is the "lazy" XPRESS compressor. Before choosing a match, it checks to * see if there's a longer match at the next position. If yes, it outputs a * literal and continues to the next position. If no, it outputs the match. */ static size_t xpress_compress_lazy(struct xpress_compressor * restrict c, const void * restrict in, size_t in_nbytes, void * restrict out, size_t out_nbytes_avail) { const u8 * const in_begin = in; const u8 * in_next = in_begin; const u8 * const in_end = in_begin + in_nbytes; struct xpress_item *next_chosen_item = c->chosen_items; unsigned len_3_too_far; u32 next_hashes[2] = {}; if (in_nbytes <= 8192) len_3_too_far = 2048; else len_3_too_far = 4096; hc_matchfinder_init(&c->hc_mf); do { unsigned cur_len; unsigned cur_offset; unsigned next_len; unsigned next_offset; /* Find the longest match at the current position. */ cur_len = hc_matchfinder_longest_match(&c->hc_mf, in_begin, in_next - in_begin, XPRESS_MIN_MATCH_LEN - 1, in_end - in_next, min(in_end - in_next, c->nice_match_length), c->max_search_depth, next_hashes, &cur_offset); in_next += 1; if (cur_len < XPRESS_MIN_MATCH_LEN || (cur_len == XPRESS_MIN_MATCH_LEN && cur_offset >= len_3_too_far)) { /* No match found. Choose a literal. */ *next_chosen_item++ = xpress_record_literal(c, *(in_next - 1)); continue; } have_cur_match: /* We have a match at the current position. */ /* If the current match is very long, choose it immediately. */ if (cur_len >= c->nice_match_length) { *next_chosen_item++ = xpress_record_match(c, cur_len, cur_offset); hc_matchfinder_skip_positions(&c->hc_mf, in_begin, in_next - in_begin, in_end - in_begin, cur_len - 1, next_hashes); in_next += cur_len - 1; continue; } /* * Try to find a match at the next position. * * Note: since we already have a match at the *current* * position, we use only half the 'max_search_depth' when * checking the *next* position. This is a useful trade-off * because it's more worthwhile to use a greater search depth on * the initial match than on the next match (since a lot of the * time, that next match won't even be used). * * Note: it's possible to structure the code such that there's * only one call to longest_match(), which handles both the * "find the initial match" and "try to find a longer match" * cases. However, it is faster to have two call sites, with * longest_match() inlined at each. */ next_len = hc_matchfinder_longest_match(&c->hc_mf, in_begin, in_next - in_begin, cur_len, in_end - in_next, min(in_end - in_next, c->nice_match_length), c->max_search_depth / 2, next_hashes, &next_offset); in_next += 1; if (next_len > cur_len) { /* Found a longer match at the next position, so output * a literal. */ *next_chosen_item++ = xpress_record_literal(c, *(in_next - 2)); cur_len = next_len; cur_offset = next_offset; goto have_cur_match; } else { /* Didn't find a longer match at the next position, so * output the current match. */ *next_chosen_item++ = xpress_record_match(c, cur_len, cur_offset); hc_matchfinder_skip_positions(&c->hc_mf, in_begin, in_next - in_begin, in_end - in_begin, cur_len - 2, next_hashes); in_next += cur_len - 2; continue; } } while (in_next != in_end); return xpress_write(c, out, out_nbytes_avail, next_chosen_item - c->chosen_items, false); } #if SUPPORT_NEAR_OPTIMAL_PARSING /* * Set Huffman symbol costs for the first optimization pass. * * It works well to assume that each Huffman symbol is equally probable. This * results in each symbol being assigned a cost of -log2(1.0/num_syms) where * 'num_syms' is the number of symbols in the alphabet. */ static void xpress_set_default_costs(struct xpress_compressor *c) { for (unsigned i = 0; i < XPRESS_NUM_SYMBOLS; i++) c->costs[i] = 9; } /* Update the cost model based on the codeword lengths @c->lens. */ static void xpress_update_costs(struct xpress_compressor *c) { for (unsigned i = 0; i < XPRESS_NUM_SYMBOLS; i++) c->costs[i] = c->lens[i] ? c->lens[i] : XPRESS_MAX_CODEWORD_LEN; } /* * Follow the minimum cost path in the graph of possible match/literal choices * and compute the frequencies of the Huffman symbols that are needed to output * those matches and literals. */ static void xpress_tally_item_list(struct xpress_compressor *c, struct xpress_optimum_node *end_node) { struct xpress_optimum_node *cur_node = c->optimum_nodes; do { unsigned length = cur_node->item & OPTIMUM_LEN_MASK; unsigned offset = cur_node->item >> OPTIMUM_OFFSET_SHIFT; if (length == 1) { /* Literal */ unsigned literal = offset; c->freqs[literal]++; } else { /* Match */ unsigned adjusted_len; unsigned log2_offset; unsigned len_hdr; unsigned sym; adjusted_len = length - XPRESS_MIN_MATCH_LEN; log2_offset = bsr32(offset); len_hdr = min(0xF, adjusted_len); sym = XPRESS_NUM_CHARS + ((log2_offset << 4) | len_hdr); c->freqs[sym]++; } cur_node += length; } while (cur_node != end_node); } /* * Find a new minimum cost path through the graph of possible match/literal * choices. We find the minimum cost path from 'c->optimum_nodes[0]', which * represents the node at the beginning of the input buffer, to * 'c->optimum_nodes[in_nbytes]', which represents the node at the end of the * input buffer. Edge costs are evaluated using the cost model 'c->costs'. * * The algorithm works backward, starting at 'c->optimum_nodes[in_nbytes]' and * proceeding backwards one position at a time. At each position, the minimum * cost to reach 'c->optimum_nodes[in_nbytes]' from that position is computed * and the match/literal choice is saved. */ static void xpress_find_min_cost_path(struct xpress_compressor *c, size_t in_nbytes, struct lz_match *end_cache_ptr) { struct xpress_optimum_node *cur_node = c->optimum_nodes + in_nbytes; struct lz_match *cache_ptr = end_cache_ptr; cur_node->cost_to_end = 0; do { unsigned literal; u32 best_item; u32 best_cost_to_end; unsigned num_matches; struct lz_match *match; unsigned len; cur_node--; cache_ptr--; literal = cache_ptr->offset; /* Consider coding a literal. */ best_item = ((u32)literal << OPTIMUM_OFFSET_SHIFT) | 1; best_cost_to_end = c->costs[literal] + (cur_node + 1)->cost_to_end; num_matches = cache_ptr->length; if (num_matches == 0) { /* No matches; the only choice is the literal. */ cur_node->cost_to_end = best_cost_to_end; cur_node->item = best_item; continue; } /* * Consider each match length from the minimum * (XPRESS_MIN_MATCH_LEN) to the length of the longest match * found at this position. For each length, consider only the * smallest offset for which that length is available. Although * this is not guaranteed to be optimal due to the possibility * of a larger offset costing less than a smaller offset to * code, this is a very useful heuristic. */ match = cache_ptr - num_matches; len = XPRESS_MIN_MATCH_LEN; if (cache_ptr[-1].length < 0xF + XPRESS_MIN_MATCH_LEN) { /* All lengths are small. Optimize accordingly. */ do { unsigned offset; unsigned log2_offset; u32 offset_cost; offset = match->offset; log2_offset = bsr32(offset); offset_cost = log2_offset; do { unsigned len_hdr; unsigned sym; u32 cost_to_end; len_hdr = len - XPRESS_MIN_MATCH_LEN; sym = XPRESS_NUM_CHARS + ((log2_offset << 4) | len_hdr); cost_to_end = offset_cost + c->costs[sym] + (cur_node + len)->cost_to_end; if (cost_to_end < best_cost_to_end) { best_cost_to_end = cost_to_end; best_item = ((u32)offset << OPTIMUM_OFFSET_SHIFT) | len; } } while (++len <= match->length); } while (++match != cache_ptr); } else { /* Some lengths are big. */ do { unsigned offset; unsigned log2_offset; u32 offset_cost; offset = match->offset; log2_offset = bsr32(offset); offset_cost = log2_offset; do { unsigned adjusted_len; unsigned len_hdr; unsigned sym; u32 cost_to_end; adjusted_len = len - XPRESS_MIN_MATCH_LEN; len_hdr = min(adjusted_len, 0xF); sym = XPRESS_NUM_CHARS + ((log2_offset << 4) | len_hdr); cost_to_end = offset_cost + c->costs[sym] + (cur_node + len)->cost_to_end; if (adjusted_len >= 0xF) { cost_to_end += 8; if (adjusted_len - 0xF >= 0xFF) cost_to_end += 16; } if (cost_to_end < best_cost_to_end) { best_cost_to_end = cost_to_end; best_item = ((u32)offset << OPTIMUM_OFFSET_SHIFT) | len; } } while (++len <= match->length); } while (++match != cache_ptr); } cache_ptr -= num_matches; cur_node->cost_to_end = best_cost_to_end; cur_node->item = best_item; } while (cur_node != c->optimum_nodes); } /* * This routine finds matches at each position in the buffer in[0...in_nbytes]. * The matches are cached in the array c->match_cache, and the return value is a * pointer past the last slot in this array that was filled. */ static struct lz_match * xpress_find_matches(struct xpress_compressor * restrict c, const void * restrict in, size_t in_nbytes) { const u8 * const in_begin = in; const u8 *in_next = in_begin; struct lz_match *cache_ptr = c->match_cache; u32 next_hashes[2] = {}; u32 max_len = in_nbytes; u32 nice_len = min(max_len, c->nice_match_length); bt_matchfinder_init(&c->bt_mf); for (;;) { struct lz_match *matches; u32 best_len; /* If we've found so many matches that the cache might overflow * if we keep finding more, then stop finding matches. This * case is very unlikely. */ if (unlikely(cache_ptr >= c->cache_overflow_mark || max_len < BT_MATCHFINDER_REQUIRED_NBYTES)) break; matches = cache_ptr; /* Find matches with the current position using the binary tree * matchfinder and save them in the next available slots in * the match cache. */ cache_ptr = bt_matchfinder_get_matches(&c->bt_mf, in_begin, in_next - in_begin, max_len, nice_len, c->max_search_depth, next_hashes, &best_len, cache_ptr); cache_ptr->length = cache_ptr - matches; cache_ptr->offset = *in_next++; cache_ptr++; max_len--; nice_len = min(nice_len, max_len); /* * If there was a very long match found, then don't cache any * matches for the bytes covered by that match. This avoids * degenerate behavior when compressing highly redundant data, * where the number of matches can be very large. * * This heuristic doesn't actually hurt the compression ratio * very much. If there's a long match, then the data must be * highly compressible, so it doesn't matter as much what we do. */ if (best_len >= nice_len) { if (unlikely(best_len + BT_MATCHFINDER_REQUIRED_NBYTES >= max_len)) break; --best_len; do { bt_matchfinder_skip_position(&c->bt_mf, in_begin, in_next - in_begin, nice_len, c->max_search_depth, next_hashes); cache_ptr->length = 0; cache_ptr->offset = *in_next++; cache_ptr++; max_len--; nice_len = min(nice_len, max_len); } while (--best_len); } } while (max_len--) { cache_ptr->length = 0; cache_ptr->offset = *in_next++; cache_ptr++; } return cache_ptr; } /* * This is the "near-optimal" XPRESS compressor. It computes a compressed * representation of the input buffer by executing a minimum cost path search * over the graph of possible match/literal choices, assuming a certain cost for * each Huffman symbol. The result is usually close to optimal, but it is *not* * guaranteed to be optimal because of (a) heuristic restrictions in which * matches are considered, and (b) symbol costs are unknown until those symbols * have already been chosen --- so iterative optimization must be used, and the * algorithm might converge on a local optimum rather than a global optimum. */ static size_t xpress_compress_near_optimal(struct xpress_compressor * restrict c, const void * restrict in, size_t in_nbytes, void * restrict out, size_t out_nbytes_avail) { struct lz_match *end_cache_ptr; unsigned num_passes_remaining = c->num_optim_passes; /* Run the input buffer through the matchfinder and save the results. */ end_cache_ptr = xpress_find_matches(c, in, in_nbytes); /* The first optimization pass uses a default cost model. Each * additional optimization pass uses a cost model derived from the * Huffman code computed in the previous pass. */ xpress_set_default_costs(c); do { xpress_find_min_cost_path(c, in_nbytes, end_cache_ptr); xpress_tally_item_list(c, c->optimum_nodes + in_nbytes); if (num_passes_remaining > 1) { c->freqs[XPRESS_END_OF_DATA]++; xpress_make_huffman_code(c); xpress_update_costs(c); xpress_reset_symbol_frequencies(c); } } while (--num_passes_remaining); return xpress_write(c, out, out_nbytes_avail, in_nbytes, true); } #endif /* SUPPORT_NEAR_OPTIMAL_PARSING */ static size_t xpress_get_compressor_size(size_t max_bufsize, unsigned compression_level) { #if SUPPORT_NEAR_OPTIMAL_PARSING if (compression_level >= MIN_LEVEL_FOR_NEAR_OPTIMAL) return offsetof(struct xpress_compressor, bt_mf) + bt_matchfinder_size(max_bufsize); #endif return offsetof(struct xpress_compressor, hc_mf) + hc_matchfinder_size(max_bufsize); } static u64 xpress_get_needed_memory(size_t max_bufsize, unsigned compression_level, bool destructive) { u64 size = 0; if (max_bufsize > XPRESS_MAX_BUFSIZE) return 0; size += xpress_get_compressor_size(max_bufsize, compression_level); if (compression_level < MIN_LEVEL_FOR_NEAR_OPTIMAL || !SUPPORT_NEAR_OPTIMAL_PARSING) { /* chosen_items */ size += max_bufsize * sizeof(struct xpress_item); } #if SUPPORT_NEAR_OPTIMAL_PARSING else { /* optimum_nodes */ size += (max_bufsize + 1) * sizeof(struct xpress_optimum_node); /* match_cache */ size += ((max_bufsize * CACHE_RESERVE_PER_POS) + XPRESS_MAX_MATCH_LEN + max_bufsize) * sizeof(struct lz_match); } #endif return size; } static int xpress_create_compressor(size_t max_bufsize, unsigned compression_level, bool destructive, void **c_ret) { struct xpress_compressor *c; if (max_bufsize > XPRESS_MAX_BUFSIZE) return WIMLIB_ERR_INVALID_PARAM; c = MALLOC(xpress_get_compressor_size(max_bufsize, compression_level)); if (!c) goto oom0; if (compression_level < MIN_LEVEL_FOR_NEAR_OPTIMAL || !SUPPORT_NEAR_OPTIMAL_PARSING) { c->chosen_items = MALLOC(max_bufsize * sizeof(struct xpress_item)); if (!c->chosen_items) goto oom1; if (compression_level < 30) { c->impl = xpress_compress_greedy; c->max_search_depth = (compression_level * 30) / 16; c->nice_match_length = (compression_level * 60) / 16; } else { c->impl = xpress_compress_lazy; c->max_search_depth = (compression_level * 30) / 32; c->nice_match_length = (compression_level * 60) / 32; /* xpress_compress_lazy() needs max_search_depth >= 2 * because it halves the max_search_depth when * attempting a lazy match, and max_search_depth cannot * be 0. */ if (c->max_search_depth < 2) c->max_search_depth = 2; } } #if SUPPORT_NEAR_OPTIMAL_PARSING else { c->optimum_nodes = MALLOC((max_bufsize + 1) * sizeof(struct xpress_optimum_node)); c->match_cache = MALLOC(((max_bufsize * CACHE_RESERVE_PER_POS) + XPRESS_MAX_MATCH_LEN + max_bufsize) * sizeof(struct lz_match)); if (!c->optimum_nodes || !c->match_cache) { FREE(c->optimum_nodes); FREE(c->match_cache); goto oom1; } c->cache_overflow_mark = &c->match_cache[max_bufsize * CACHE_RESERVE_PER_POS]; c->impl = xpress_compress_near_optimal; c->max_search_depth = (compression_level * 28) / 100; c->nice_match_length = (compression_level * 56) / 100; c->num_optim_passes = compression_level / 40; } #endif /* SUPPORT_NEAR_OPTIMAL_PARSING */ /* max_search_depth == 0 is invalid. */ if (c->max_search_depth < 1) c->max_search_depth = 1; *c_ret = c; return 0; oom1: FREE(c); oom0: return WIMLIB_ERR_NOMEM; } static size_t xpress_compress(const void *restrict in, size_t in_nbytes, void *restrict out, size_t out_nbytes_avail, void *restrict _c) { struct xpress_compressor *c = _c; /* Don't bother trying to compress very small inputs. */ if (in_nbytes < 25) return 0; if (out_nbytes_avail <= XPRESS_NUM_SYMBOLS / 2 + 4) return 0; xpress_reset_symbol_frequencies(c); return (*c->impl)(c, in, in_nbytes, out, out_nbytes_avail); } static void xpress_free_compressor(void *_c) { struct xpress_compressor *c = _c; #if SUPPORT_NEAR_OPTIMAL_PARSING if (c->impl == xpress_compress_near_optimal) { FREE(c->optimum_nodes); FREE(c->match_cache); } else #endif FREE(c->chosen_items); FREE(c); } const struct compressor_ops xpress_compressor_ops = { .get_needed_memory = xpress_get_needed_memory, .create_compressor = xpress_create_compressor, .compress = xpress_compress, .free_compressor = xpress_free_compressor, }; wimlib-1.13.1/src/lzms_compress.c0000644000175000017500000021375113454503760013654 00000000000000/* * lzms_compress.c * * A compressor for the LZMS compression format. */ /* * Copyright (C) 2013, 2014, 2015 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include "wimlib/compress_common.h" #include "wimlib/compressor_ops.h" #include "wimlib/error.h" #include "wimlib/lcpit_matchfinder.h" #include "wimlib/lz_extend.h" #include "wimlib/lz_hash.h" #include "wimlib/lzms_common.h" #include "wimlib/unaligned.h" #include "wimlib/util.h" /* * MAX_FAST_LENGTH is the maximum match length for which the length slot can be * looked up directly in 'fast_length_slot_tab' and the length cost can be * looked up directly in 'fast_length_cost_tab'. * * We also limit the 'nice_match_len' parameter to this value. Consequently, if * the longest match found is shorter than 'nice_match_len', then it must also * be shorter than MAX_FAST_LENGTH. This makes it possible to do fast lookups * of length costs using 'fast_length_cost_tab' without having to keep checking * whether the length exceeds MAX_FAST_LENGTH or not. */ #define MAX_FAST_LENGTH 255 /* NUM_OPTIM_NODES is the maximum number of bytes the parsing algorithm will * step forward before forcing the pending items to be encoded. If this value * is increased, then there will be fewer forced flushes, but the probability * entries and Huffman codes will be more likely to become outdated. */ #define NUM_OPTIM_NODES 2048 /* COST_SHIFT is a scaling factor that makes it possible to consider fractional * bit costs. A single bit has a cost of (1 << COST_SHIFT). */ #define COST_SHIFT 6 /* Length of the hash table for finding delta matches */ #define DELTA_HASH_ORDER 17 #define DELTA_HASH_LENGTH ((u32)1 << DELTA_HASH_ORDER) /* The number of bytes to hash when finding delta matches; also taken to be the * minimum length of an explicit offset delta match */ #define NBYTES_HASHED_FOR_DELTA 3 /* The number of delta match powers to consider (must be <= * LZMS_NUM_DELTA_POWER_SYMS) */ #define NUM_POWERS_TO_CONSIDER 6 /* This structure tracks the state of writing bits as a series of 16-bit coding * units, starting at the end of the output buffer and proceeding backwards. */ struct lzms_output_bitstream { /* Bits that haven't yet been written to the output buffer */ u64 bitbuf; /* Number of bits currently held in @bitbuf */ unsigned bitcount; /* Pointer to the beginning of the output buffer (this is the "end" when * writing backwards!) */ u8 *begin; /* Pointer to just past the next position in the output buffer at which * to output a 16-bit coding unit */ u8 *next; }; /* This structure tracks the state of range encoding and its output, which * starts at the beginning of the output buffer and proceeds forwards. */ struct lzms_range_encoder { /* The lower boundary of the current range. Logically, this is a 33-bit * integer whose high bit is needed to detect carries. */ u64 lower_bound; /* The size of the current range */ u32 range_size; /* The next 16-bit coding unit to output */ u16 cache; /* The number of 16-bit coding units whose output has been delayed due * to possible carrying. The first such coding unit is @cache; all * subsequent such coding units are 0xffff. */ u32 cache_size; /* Pointer to the beginning of the output buffer */ u8 *begin; /* Pointer to the position in the output buffer at which the next coding * unit must be written */ u8 *next; /* Pointer to just past the end of the output buffer */ u8 *end; }; /* Bookkeeping information for an adaptive Huffman code */ struct lzms_huffman_rebuild_info { /* The remaining number of symbols to encode until this code must be * rebuilt */ unsigned num_syms_until_rebuild; /* The number of symbols in this code */ unsigned num_syms; /* The rebuild frequency of this code, in symbols */ unsigned rebuild_freq; /* The Huffman codeword of each symbol in this code */ u32 *codewords; /* The length of each Huffman codeword, in bits */ u8 *lens; /* The frequency of each symbol in this code */ u32 *freqs; }; /* * The compressor-internal representation of a match or literal. * * Literals have length=1; matches have length > 1. (We disallow matches of * length 1, even though this is a valid length in LZMS.) * * The source is encoded as follows: * * - Literals: the literal byte itself * - Explicit offset LZ matches: the match offset plus (LZMS_NUM_LZ_REPS - 1) * - Repeat offset LZ matches: the index of the offset in recent_lz_offsets * - Explicit offset delta matches: DELTA_SOURCE_TAG is set, the next 3 bits are * the power, and the remainder is the raw offset plus (LZMS_NUM_DELTA_REPS-1) * - Repeat offset delta matches: DELTA_SOURCE_TAG is set, and the remainder is * the index of the (power, raw_offset) pair in recent_delta_pairs */ struct lzms_item { u32 length; u32 source; }; #define DELTA_SOURCE_TAG ((u32)1 << 31) #define DELTA_SOURCE_POWER_SHIFT 28 #define DELTA_SOURCE_RAW_OFFSET_MASK (((u32)1 << DELTA_SOURCE_POWER_SHIFT) - 1) static _unused_attribute void check_that_powers_fit_in_bitfield(void) { STATIC_ASSERT(LZMS_NUM_DELTA_POWER_SYMS <= (1 << (31 - DELTA_SOURCE_POWER_SHIFT))); } /* A stripped-down version of the adaptive state in LZMS which excludes the * probability entries and Huffman codes */ struct lzms_adaptive_state { /* Recent offsets for LZ matches */ u32 recent_lz_offsets[LZMS_NUM_LZ_REPS + 1]; u32 prev_lz_offset; /* 0 means none */ u32 upcoming_lz_offset; /* 0 means none */ /* Recent (power, raw offset) pairs for delta matches. * The low DELTA_SOURCE_POWER_SHIFT bits of each entry are the raw * offset, and the high bits are the power. */ u32 recent_delta_pairs[LZMS_NUM_DELTA_REPS + 1]; u32 prev_delta_pair; /* 0 means none */ u32 upcoming_delta_pair; /* 0 means none */ /* States for predicting the probabilities of item types */ u8 main_state; u8 match_state; u8 lz_state; u8 lz_rep_states[LZMS_NUM_LZ_REP_DECISIONS]; u8 delta_state; u8 delta_rep_states[LZMS_NUM_DELTA_REP_DECISIONS]; } _aligned_attribute(64); /* * This structure represents a byte position in the preprocessed input data and * a node in the graph of possible match/literal choices. * * Logically, each incoming edge to this node is labeled with a literal or a * match that can be taken to reach this position from an earlier position; and * each outgoing edge from this node is labeled with a literal or a match that * can be taken to advance from this position to a later position. */ struct lzms_optimum_node { /* * The cost of the lowest-cost path that has been found to reach this * position. This can change as progressively lower cost paths are * found to reach this position. */ u32 cost; #define INFINITE_COST UINT32_MAX /* * @item is the last item that was taken to reach this position to reach * it with the stored @cost. This can change as progressively lower * cost paths are found to reach this position. * * In some cases we look ahead more than one item. If we looked ahead n * items to reach this position, then @item is the last item taken, * @extra_items contains the other items ordered from second-to-last to * first, and @num_extra_items is n - 1. */ unsigned num_extra_items; struct lzms_item item; struct lzms_item extra_items[2]; /* * The adaptive state that exists at this position. This is filled in * lazily, only after the minimum-cost path to this position is found. * * Note: the way the algorithm handles this adaptive state in the * "minimum-cost" parse is actually only an approximation. It's * possible for the globally optimal, minimum cost path to contain a * prefix, ending at a position, where that path prefix is *not* the * minimum cost path to that position. This can happen if such a path * prefix results in a different adaptive state which results in lower * costs later. Although the algorithm does do some heuristic * multi-item lookaheads, it does not solve this problem in general. * * Note: this adaptive state structure also does not include the * probability entries or current Huffman codewords. Those aren't * maintained per-position and are only updated occasionally. */ struct lzms_adaptive_state state; } _aligned_attribute(64); /* The main compressor structure */ struct lzms_compressor { /* The matchfinder for LZ matches */ struct lcpit_matchfinder mf; /* The preprocessed buffer of data being compressed */ u8 *in_buffer; /* The number of bytes of data to be compressed, which is the number of * bytes of data in @in_buffer that are actually valid */ size_t in_nbytes; /* * Boolean flags to enable consideration of various types of multi-step * operations during parsing. * * Among other cases, multi-step operations can help with gaps where two * matches are separated by a non-matching byte. * * This idea is borrowed from Igor Pavlov's LZMA encoder. */ bool try_lit_lzrep0; bool try_lzrep_lit_lzrep0; bool try_lzmatch_lit_lzrep0; /* * If true, the compressor can use delta matches. This slows down * compression. It improves the compression ratio greatly, slightly, or * not at all, depending on the input data. */ bool use_delta_matches; /* If true, the compressor need not preserve the input buffer if it * compresses the data successfully. */ bool destructive; /* 'last_target_usages' is a large array that is only needed for * preprocessing, so it is in union with fields that don't need to be * initialized until after preprocessing. */ union { struct { /* Temporary space to store matches found by the LZ matchfinder */ struct lz_match matches[MAX_FAST_LENGTH - LZMS_MIN_MATCH_LENGTH + 1]; /* Hash table for finding delta matches */ u32 delta_hash_table[DELTA_HASH_LENGTH]; /* For each delta power, the hash code for the next sequence */ u32 next_delta_hashes[NUM_POWERS_TO_CONSIDER]; /* The per-byte graph nodes for near-optimal parsing */ struct lzms_optimum_node optimum_nodes[NUM_OPTIM_NODES + MAX_FAST_LENGTH + 1 + MAX_FAST_LENGTH]; /* Table: length => current cost for small match lengths */ u32 fast_length_cost_tab[MAX_FAST_LENGTH + 1]; /* Range encoder which outputs to the beginning of the compressed data * buffer, proceeding forwards */ struct lzms_range_encoder rc; /* Bitstream which outputs to the end of the compressed data buffer, * proceeding backwards */ struct lzms_output_bitstream os; /* States and probability entries for item type disambiguation */ unsigned main_state; unsigned match_state; unsigned lz_state; unsigned lz_rep_states[LZMS_NUM_LZ_REP_DECISIONS]; unsigned delta_state; unsigned delta_rep_states[LZMS_NUM_DELTA_REP_DECISIONS]; struct lzms_probabilites probs; /* Huffman codes */ struct lzms_huffman_rebuild_info literal_rebuild_info; u32 literal_codewords[LZMS_NUM_LITERAL_SYMS]; u8 literal_lens[LZMS_NUM_LITERAL_SYMS]; u32 literal_freqs[LZMS_NUM_LITERAL_SYMS]; struct lzms_huffman_rebuild_info lz_offset_rebuild_info; u32 lz_offset_codewords[LZMS_MAX_NUM_OFFSET_SYMS]; u8 lz_offset_lens[LZMS_MAX_NUM_OFFSET_SYMS]; u32 lz_offset_freqs[LZMS_MAX_NUM_OFFSET_SYMS]; struct lzms_huffman_rebuild_info length_rebuild_info; u32 length_codewords[LZMS_NUM_LENGTH_SYMS]; u8 length_lens[LZMS_NUM_LENGTH_SYMS]; u32 length_freqs[LZMS_NUM_LENGTH_SYMS]; struct lzms_huffman_rebuild_info delta_offset_rebuild_info; u32 delta_offset_codewords[LZMS_MAX_NUM_OFFSET_SYMS]; u8 delta_offset_lens[LZMS_MAX_NUM_OFFSET_SYMS]; u32 delta_offset_freqs[LZMS_MAX_NUM_OFFSET_SYMS]; struct lzms_huffman_rebuild_info delta_power_rebuild_info; u32 delta_power_codewords[LZMS_NUM_DELTA_POWER_SYMS]; u8 delta_power_lens[LZMS_NUM_DELTA_POWER_SYMS]; u32 delta_power_freqs[LZMS_NUM_DELTA_POWER_SYMS]; }; /* struct */ s32 last_target_usages[65536]; }; /* union */ /* Table: length => length slot for small match lengths */ u8 fast_length_slot_tab[MAX_FAST_LENGTH + 1]; /* Tables for mapping offsets to offset slots */ /* slots [0, 167); 0 <= num_extra_bits <= 10 */ u8 offset_slot_tab_1[0xe4a5]; /* slots [167, 427); 11 <= num_extra_bits <= 15 */ u16 offset_slot_tab_2[0x3d0000 >> 11]; /* slots [427, 799); 16 <= num_extra_bits */ u16 offset_slot_tab_3[((LZMS_MAX_MATCH_OFFSET + 1) - 0xe4a5) >> 16]; }; /****************************************************************************** * Offset and length slot acceleration * ******************************************************************************/ /* Generate the acceleration table for length slots. */ static void lzms_init_fast_length_slot_tab(struct lzms_compressor *c) { unsigned slot = 0; for (u32 len = LZMS_MIN_MATCH_LENGTH; len <= MAX_FAST_LENGTH; len++) { if (len >= lzms_length_slot_base[slot + 1]) slot++; c->fast_length_slot_tab[len] = slot; } } /* Generate the acceleration tables for offset slots. */ static void lzms_init_offset_slot_tabs(struct lzms_compressor *c) { u32 offset; unsigned slot = 0; /* slots [0, 167); 0 <= num_extra_bits <= 10 */ for (offset = 1; offset < 0xe4a5; offset++) { if (offset >= lzms_offset_slot_base[slot + 1]) slot++; c->offset_slot_tab_1[offset] = slot; } /* slots [167, 427); 11 <= num_extra_bits <= 15 */ for (; offset < 0x3de4a5; offset += (u32)1 << 11) { if (offset >= lzms_offset_slot_base[slot + 1]) slot++; c->offset_slot_tab_2[(offset - 0xe4a5) >> 11] = slot; } /* slots [427, 799); 16 <= num_extra_bits */ for (; offset < LZMS_MAX_MATCH_OFFSET + 1; offset += (u32)1 << 16) { if (offset >= lzms_offset_slot_base[slot + 1]) slot++; c->offset_slot_tab_3[(offset - 0xe4a5) >> 16] = slot; } } /* * Return the length slot for the specified match length, using the compressor's * acceleration table if the length is small enough. */ static forceinline unsigned lzms_comp_get_length_slot(const struct lzms_compressor *c, u32 length) { if (likely(length <= MAX_FAST_LENGTH)) return c->fast_length_slot_tab[length]; return lzms_get_length_slot(length); } /* * Return the offset slot for the specified match offset, using the compressor's * acceleration tables to speed up the mapping. */ static forceinline unsigned lzms_comp_get_offset_slot(const struct lzms_compressor *c, u32 offset) { if (offset < 0xe4a5) return c->offset_slot_tab_1[offset]; offset -= 0xe4a5; if (offset < 0x3d0000) return c->offset_slot_tab_2[offset >> 11]; return c->offset_slot_tab_3[offset >> 16]; } /****************************************************************************** * Range encoding * ******************************************************************************/ /* * Initialize the range encoder @rc to write forwards to the specified buffer * @out that is @size bytes long. */ static void lzms_range_encoder_init(struct lzms_range_encoder *rc, u8 *out, size_t size) { rc->lower_bound = 0; rc->range_size = 0xffffffff; rc->cache = 0; rc->cache_size = 1; rc->begin = out; rc->next = out - sizeof(le16); rc->end = out + (size & ~1); } /* * Attempt to flush bits from the range encoder. * * The basic idea is that we're writing bits from @rc->lower_bound to the * output. However, due to carrying, the writing of coding units with the * maximum value, as well as one prior coding unit, must be delayed until it is * determined whether a carry is needed. * * This is based on the public domain code for LZMA written by Igor Pavlov, but * with the following differences: * * - In LZMS, 16-bit coding units are required rather than 8-bit. * * - In LZMS, the first coding unit is not ignored by the decompressor, so * the encoder cannot output a dummy value to that position. */ static void lzms_range_encoder_shift_low(struct lzms_range_encoder *rc) { if ((u32)(rc->lower_bound) < 0xffff0000 || (u32)(rc->lower_bound >> 32) != 0) { /* Carry not needed (rc->lower_bound < 0xffff0000), or carry * occurred ((rc->lower_bound >> 32) != 0, a.k.a. the carry bit * is 1). */ do { if (likely(rc->next >= rc->begin)) { if (rc->next != rc->end) { put_unaligned_le16(rc->cache + (u16)(rc->lower_bound >> 32), rc->next); rc->next += sizeof(le16); } } else { rc->next += sizeof(le16); } rc->cache = 0xffff; } while (--rc->cache_size != 0); rc->cache = (rc->lower_bound >> 16) & 0xffff; } ++rc->cache_size; rc->lower_bound = (rc->lower_bound & 0xffff) << 16; } static bool lzms_range_encoder_flush(struct lzms_range_encoder *rc) { for (int i = 0; i < 4; i++) lzms_range_encoder_shift_low(rc); return rc->next != rc->end; } /* * Encode the next bit using the range encoder. * * @prob is the probability out of LZMS_PROBABILITY_DENOMINATOR that the next * bit is 0 rather than 1. */ static forceinline void lzms_range_encode_bit(struct lzms_range_encoder *rc, int bit, u32 prob) { /* Normalize if needed. */ if (rc->range_size <= 0xffff) { rc->range_size <<= 16; lzms_range_encoder_shift_low(rc); } u32 bound = (rc->range_size >> LZMS_PROBABILITY_BITS) * prob; if (bit == 0) { rc->range_size = bound; } else { rc->lower_bound += bound; rc->range_size -= bound; } } /* * Encode a bit. This wraps around lzms_range_encode_bit() to handle using and * updating the state and its corresponding probability entry. */ static forceinline void lzms_encode_bit(int bit, unsigned *state_p, unsigned num_states, struct lzms_probability_entry *probs, struct lzms_range_encoder *rc) { struct lzms_probability_entry *prob_entry; u32 prob; /* Load the probability entry for the current state. */ prob_entry = &probs[*state_p]; /* Update the state based on the next bit. */ *state_p = ((*state_p << 1) | bit) & (num_states - 1); /* Get the probability that the bit is 0. */ prob = lzms_get_probability(prob_entry); /* Update the probability entry. */ lzms_update_probability_entry(prob_entry, bit); /* Encode the bit using the range encoder. */ lzms_range_encode_bit(rc, bit, prob); } /* Helper functions for encoding bits in the various decision classes */ static void lzms_encode_main_bit(struct lzms_compressor *c, int bit) { lzms_encode_bit(bit, &c->main_state, LZMS_NUM_MAIN_PROBS, c->probs.main, &c->rc); } static void lzms_encode_match_bit(struct lzms_compressor *c, int bit) { lzms_encode_bit(bit, &c->match_state, LZMS_NUM_MATCH_PROBS, c->probs.match, &c->rc); } static void lzms_encode_lz_bit(struct lzms_compressor *c, int bit) { lzms_encode_bit(bit, &c->lz_state, LZMS_NUM_LZ_PROBS, c->probs.lz, &c->rc); } static void lzms_encode_lz_rep_bit(struct lzms_compressor *c, int bit, int idx) { lzms_encode_bit(bit, &c->lz_rep_states[idx], LZMS_NUM_LZ_REP_PROBS, c->probs.lz_rep[idx], &c->rc); } static void lzms_encode_delta_bit(struct lzms_compressor *c, int bit) { lzms_encode_bit(bit, &c->delta_state, LZMS_NUM_DELTA_PROBS, c->probs.delta, &c->rc); } static void lzms_encode_delta_rep_bit(struct lzms_compressor *c, int bit, int idx) { lzms_encode_bit(bit, &c->delta_rep_states[idx], LZMS_NUM_DELTA_REP_PROBS, c->probs.delta_rep[idx], &c->rc); } /****************************************************************************** * Huffman encoding and verbatim bits * ******************************************************************************/ /* * Initialize the output bitstream @os to write backwards to the specified * buffer @out that is @size bytes long. */ static void lzms_output_bitstream_init(struct lzms_output_bitstream *os, u8 *out, size_t size) { os->bitbuf = 0; os->bitcount = 0; os->begin = out; os->next = out + (size & ~1); } /* * Write some bits, contained in the low-order @num_bits bits of @bits, to the * output bitstream @os. * * @max_num_bits is a compile-time constant that specifies the maximum number of * bits that can ever be written at this call site. */ static forceinline void lzms_write_bits(struct lzms_output_bitstream *os, const u32 bits, const unsigned num_bits, const unsigned max_num_bits) { /* Add the bits to the bit buffer variable. */ os->bitcount += num_bits; os->bitbuf = (os->bitbuf << num_bits) | bits; /* Check whether any coding units need to be written. */ while (os->bitcount >= 16) { os->bitcount -= 16; /* Write a coding unit, unless it would underflow the buffer. */ if (os->next != os->begin) { os->next -= sizeof(le16); put_unaligned_le16(os->bitbuf >> os->bitcount, os->next); } /* Optimization for call sites that never write more than 16 * bits at once. */ if (max_num_bits <= 16) break; } } /* * Flush the output bitstream, ensuring that all bits written to it have been * written to memory. Return %true if all bits have been output successfully, * or %false if an overrun occurred. */ static bool lzms_output_bitstream_flush(struct lzms_output_bitstream *os) { if (os->next == os->begin) return false; if (os->bitcount != 0) { os->next -= sizeof(le16); put_unaligned_le16(os->bitbuf << (16 - os->bitcount), os->next); } return true; } static void lzms_build_huffman_code(struct lzms_huffman_rebuild_info *rebuild_info) { make_canonical_huffman_code(rebuild_info->num_syms, LZMS_MAX_CODEWORD_LENGTH, rebuild_info->freqs, rebuild_info->lens, rebuild_info->codewords); rebuild_info->num_syms_until_rebuild = rebuild_info->rebuild_freq; } static void lzms_init_huffman_code(struct lzms_huffman_rebuild_info *rebuild_info, unsigned num_syms, unsigned rebuild_freq, u32 *codewords, u8 *lens, u32 *freqs) { rebuild_info->num_syms = num_syms; rebuild_info->rebuild_freq = rebuild_freq; rebuild_info->codewords = codewords; rebuild_info->lens = lens; rebuild_info->freqs = freqs; lzms_init_symbol_frequencies(freqs, num_syms); lzms_build_huffman_code(rebuild_info); } static void lzms_rebuild_huffman_code(struct lzms_huffman_rebuild_info *rebuild_info) { lzms_build_huffman_code(rebuild_info); lzms_dilute_symbol_frequencies(rebuild_info->freqs, rebuild_info->num_syms); } /* * Encode a symbol using the specified Huffman code. Then, if the Huffman code * needs to be rebuilt, rebuild it and return true; otherwise return false. */ static forceinline bool lzms_huffman_encode_symbol(unsigned sym, const u32 *codewords, const u8 *lens, u32 *freqs, struct lzms_output_bitstream *os, struct lzms_huffman_rebuild_info *rebuild_info) { lzms_write_bits(os, codewords[sym], lens[sym], LZMS_MAX_CODEWORD_LENGTH); ++freqs[sym]; if (--rebuild_info->num_syms_until_rebuild == 0) { lzms_rebuild_huffman_code(rebuild_info); return true; } return false; } /* Helper routines to encode symbols using the various Huffman codes */ static bool lzms_encode_literal_symbol(struct lzms_compressor *c, unsigned sym) { return lzms_huffman_encode_symbol(sym, c->literal_codewords, c->literal_lens, c->literal_freqs, &c->os, &c->literal_rebuild_info); } static bool lzms_encode_lz_offset_symbol(struct lzms_compressor *c, unsigned sym) { return lzms_huffman_encode_symbol(sym, c->lz_offset_codewords, c->lz_offset_lens, c->lz_offset_freqs, &c->os, &c->lz_offset_rebuild_info); } static bool lzms_encode_length_symbol(struct lzms_compressor *c, unsigned sym) { return lzms_huffman_encode_symbol(sym, c->length_codewords, c->length_lens, c->length_freqs, &c->os, &c->length_rebuild_info); } static bool lzms_encode_delta_offset_symbol(struct lzms_compressor *c, unsigned sym) { return lzms_huffman_encode_symbol(sym, c->delta_offset_codewords, c->delta_offset_lens, c->delta_offset_freqs, &c->os, &c->delta_offset_rebuild_info); } static bool lzms_encode_delta_power_symbol(struct lzms_compressor *c, unsigned sym) { return lzms_huffman_encode_symbol(sym, c->delta_power_codewords, c->delta_power_lens, c->delta_power_freqs, &c->os, &c->delta_power_rebuild_info); } static void lzms_update_fast_length_costs(struct lzms_compressor *c); /* * Encode a match length. If this causes the Huffman code for length symbols to * be rebuilt, also update the length costs array used by the parser. */ static void lzms_encode_length(struct lzms_compressor *c, u32 length) { unsigned slot = lzms_comp_get_length_slot(c, length); if (lzms_encode_length_symbol(c, slot)) lzms_update_fast_length_costs(c); lzms_write_bits(&c->os, length - lzms_length_slot_base[slot], lzms_extra_length_bits[slot], LZMS_MAX_EXTRA_LENGTH_BITS); } /* Encode the offset of an LZ match. */ static void lzms_encode_lz_offset(struct lzms_compressor *c, u32 offset) { unsigned slot = lzms_comp_get_offset_slot(c, offset); lzms_encode_lz_offset_symbol(c, slot); lzms_write_bits(&c->os, offset - lzms_offset_slot_base[slot], lzms_extra_offset_bits[slot], LZMS_MAX_EXTRA_OFFSET_BITS); } /* Encode the raw offset of a delta match. */ static void lzms_encode_delta_raw_offset(struct lzms_compressor *c, u32 raw_offset) { unsigned slot = lzms_comp_get_offset_slot(c, raw_offset); lzms_encode_delta_offset_symbol(c, slot); lzms_write_bits(&c->os, raw_offset - lzms_offset_slot_base[slot], lzms_extra_offset_bits[slot], LZMS_MAX_EXTRA_OFFSET_BITS); } /****************************************************************************** * Item encoding * ******************************************************************************/ /* Encode the specified item, which may be a literal or any type of match. */ static void lzms_encode_item(struct lzms_compressor *c, u32 length, u32 source) { /* Main bit: 0 = literal, 1 = match */ int main_bit = (length > 1); lzms_encode_main_bit(c, main_bit); if (!main_bit) { /* Literal */ unsigned literal = source; lzms_encode_literal_symbol(c, literal); } else { /* Match */ /* Match bit: 0 = LZ match, 1 = delta match */ int match_bit = (source & DELTA_SOURCE_TAG) != 0; lzms_encode_match_bit(c, match_bit); if (!match_bit) { /* LZ match */ /* LZ bit: 0 = explicit offset, 1 = repeat offset */ int lz_bit = (source < LZMS_NUM_LZ_REPS); lzms_encode_lz_bit(c, lz_bit); if (!lz_bit) { /* Explicit offset LZ match */ u32 offset = source - (LZMS_NUM_LZ_REPS - 1); lzms_encode_lz_offset(c, offset); } else { /* Repeat offset LZ match */ int rep_idx = source; for (int i = 0; i < rep_idx; i++) lzms_encode_lz_rep_bit(c, 1, i); if (rep_idx < LZMS_NUM_LZ_REP_DECISIONS) lzms_encode_lz_rep_bit(c, 0, rep_idx); } } else { /* Delta match */ source &= ~DELTA_SOURCE_TAG; /* Delta bit: 0 = explicit offset, 1 = repeat offset */ int delta_bit = (source < LZMS_NUM_DELTA_REPS); lzms_encode_delta_bit(c, delta_bit); if (!delta_bit) { /* Explicit offset delta match */ u32 power = source >> DELTA_SOURCE_POWER_SHIFT; u32 raw_offset = (source & DELTA_SOURCE_RAW_OFFSET_MASK) - (LZMS_NUM_DELTA_REPS - 1); lzms_encode_delta_power_symbol(c, power); lzms_encode_delta_raw_offset(c, raw_offset); } else { /* Repeat offset delta match */ int rep_idx = source; for (int i = 0; i < rep_idx; i++) lzms_encode_delta_rep_bit(c, 1, i); if (rep_idx < LZMS_NUM_DELTA_REP_DECISIONS) lzms_encode_delta_rep_bit(c, 0, rep_idx); } } /* Match length (encoded the same way for any match type) */ lzms_encode_length(c, length); } } /* Encode a list of matches and literals chosen by the parsing algorithm. */ static void lzms_encode_nonempty_item_list(struct lzms_compressor *c, struct lzms_optimum_node *end_node) { /* Since we've stored at each node the item we took to arrive at that * node, we can trace our chosen path in backwards order. However, for * encoding we need to trace our chosen path in forwards order. To make * this possible, the following loop moves the items from their * destination nodes to their source nodes, which effectively reverses * the path. (Think of it like reversing a singly-linked list.) */ struct lzms_optimum_node *cur_node = end_node; struct lzms_item saved_item = cur_node->item; do { struct lzms_item item = saved_item; if (cur_node->num_extra_items > 0) { /* Handle an arrival via multi-item lookahead. */ unsigned i = 0; struct lzms_optimum_node *orig_node = cur_node; do { cur_node -= item.length; cur_node->item = item; item = orig_node->extra_items[i]; } while (++i != orig_node->num_extra_items); } cur_node -= item.length; saved_item = cur_node->item; cur_node->item = item; } while (cur_node != c->optimum_nodes); /* Now trace the chosen path in forwards order, encoding each item. */ do { lzms_encode_item(c, cur_node->item.length, cur_node->item.source); cur_node += cur_node->item.length; } while (cur_node != end_node); } static forceinline void lzms_encode_item_list(struct lzms_compressor *c, struct lzms_optimum_node *end_node) { if (end_node != c->optimum_nodes) lzms_encode_nonempty_item_list(c, end_node); } /****************************************************************************** * Cost evaluation * ******************************************************************************/ /* * If p is the predicted probability of the next bit being a 0, then the number * of bits required to encode a 0 bit using a binary range encoder is the real * number -log2(p), and the number of bits required to encode a 1 bit is the * real number -log2(1 - p). To avoid computing either of these expressions at * runtime, 'lzms_bit_costs' is a precomputed table that stores a mapping from * probability to cost for each possible probability. Specifically, the array * indices are the numerators of the possible probabilities in LZMS, where the * denominators are LZMS_PROBABILITY_DENOMINATOR; and the stored costs are the * bit costs multiplied by 1< static void lzms_compute_bit_costs(void) { for (u32 i = 0; i <= LZMS_PROBABILITY_DENOMINATOR; i++) { u32 prob = i; if (prob == 0) prob++; else if (prob == LZMS_PROBABILITY_DENOMINATOR) prob--; lzms_bit_costs[i] = round(-log2((double)prob / LZMS_PROBABILITY_DENOMINATOR) * (1 << COST_SHIFT)); } } #endif /* Return the cost to encode a 0 bit in the specified context. */ static forceinline u32 lzms_bit_0_cost(unsigned state, const struct lzms_probability_entry *probs) { return lzms_bit_costs[probs[state].num_recent_zero_bits]; } /* Return the cost to encode a 1 bit in the specified context. */ static forceinline u32 lzms_bit_1_cost(unsigned state, const struct lzms_probability_entry *probs) { return lzms_bit_costs[LZMS_PROBABILITY_DENOMINATOR - probs[state].num_recent_zero_bits]; } /* Return the cost to encode a literal, including the main bit. */ static forceinline u32 lzms_literal_cost(struct lzms_compressor *c, unsigned main_state, unsigned literal) { return lzms_bit_0_cost(main_state, c->probs.main) + ((u32)c->literal_lens[literal] << COST_SHIFT); } /* Update 'fast_length_cost_tab' to use the latest Huffman code. */ static void lzms_update_fast_length_costs(struct lzms_compressor *c) { int slot = -1; u32 cost = 0; for (u32 len = LZMS_MIN_MATCH_LENGTH; len <= MAX_FAST_LENGTH; len++) { if (len >= lzms_length_slot_base[slot + 1]) { slot++; cost = (u32)(c->length_lens[slot] + lzms_extra_length_bits[slot]) << COST_SHIFT; } c->fast_length_cost_tab[len] = cost; } } /* Return the cost to encode the specified match length, which must not exceed * MAX_FAST_LENGTH. */ static forceinline u32 lzms_fast_length_cost(const struct lzms_compressor *c, u32 length) { return c->fast_length_cost_tab[length]; } /* Return the cost to encode the specified LZ match offset. */ static forceinline u32 lzms_lz_offset_cost(const struct lzms_compressor *c, u32 offset) { unsigned slot = lzms_comp_get_offset_slot(c, offset); u32 num_bits = c->lz_offset_lens[slot] + lzms_extra_offset_bits[slot]; return num_bits << COST_SHIFT; } /* Return the cost to encode the specified delta power and raw offset. */ static forceinline u32 lzms_delta_source_cost(const struct lzms_compressor *c, u32 power, u32 raw_offset) { unsigned slot = lzms_comp_get_offset_slot(c, raw_offset); u32 num_bits = c->delta_power_lens[power] + c->delta_offset_lens[slot] + lzms_extra_offset_bits[slot]; return num_bits << COST_SHIFT; } /****************************************************************************** * Adaptive state * ******************************************************************************/ static void lzms_init_adaptive_state(struct lzms_adaptive_state *state) { for (int i = 0; i < LZMS_NUM_LZ_REPS + 1; i++) state->recent_lz_offsets[i] = i + 1; state->prev_lz_offset = 0; state->upcoming_lz_offset = 0; for (int i = 0; i < LZMS_NUM_DELTA_REPS + 1; i++) state->recent_delta_pairs[i] = i + 1; state->prev_delta_pair = 0; state->upcoming_delta_pair = 0; state->main_state = 0; state->match_state = 0; state->lz_state = 0; for (int i = 0; i < LZMS_NUM_LZ_REP_DECISIONS; i++) state->lz_rep_states[i] = 0; state->delta_state = 0; for (int i = 0; i < LZMS_NUM_DELTA_REP_DECISIONS; i++) state->delta_rep_states[i] = 0; } /* * Update the LRU queues for match sources when advancing by one item. * * Note: using LZMA as a point of comparison, the LRU queues in LZMS are more * complex because: * - there are separate queues for LZ and delta matches * - updates to the queues are delayed by one encoded item (this prevents * sources from being bumped up to index 0 too early) */ static void lzms_update_lru_queues(struct lzms_adaptive_state *state) { if (state->prev_lz_offset != 0) { for (int i = LZMS_NUM_LZ_REPS - 1; i >= 0; i--) state->recent_lz_offsets[i + 1] = state->recent_lz_offsets[i]; state->recent_lz_offsets[0] = state->prev_lz_offset; } state->prev_lz_offset = state->upcoming_lz_offset; if (state->prev_delta_pair != 0) { for (int i = LZMS_NUM_DELTA_REPS - 1; i >= 0; i--) state->recent_delta_pairs[i + 1] = state->recent_delta_pairs[i]; state->recent_delta_pairs[0] = state->prev_delta_pair; } state->prev_delta_pair = state->upcoming_delta_pair; } static forceinline void lzms_update_state(u8 *state_p, int bit, unsigned num_states) { *state_p = ((*state_p << 1) | bit) & (num_states - 1); } static forceinline void lzms_update_main_state(struct lzms_adaptive_state *state, int is_match) { lzms_update_state(&state->main_state, is_match, LZMS_NUM_MAIN_PROBS); } static forceinline void lzms_update_match_state(struct lzms_adaptive_state *state, int is_delta) { lzms_update_state(&state->match_state, is_delta, LZMS_NUM_MATCH_PROBS); } static forceinline void lzms_update_lz_state(struct lzms_adaptive_state *state, int is_rep) { lzms_update_state(&state->lz_state, is_rep, LZMS_NUM_LZ_PROBS); } static forceinline void lzms_update_lz_rep_states(struct lzms_adaptive_state *state, int rep_idx) { for (int i = 0; i < rep_idx; i++) lzms_update_state(&state->lz_rep_states[i], 1, LZMS_NUM_LZ_REP_PROBS); if (rep_idx < LZMS_NUM_LZ_REP_DECISIONS) lzms_update_state(&state->lz_rep_states[rep_idx], 0, LZMS_NUM_LZ_REP_PROBS); } static forceinline void lzms_update_delta_state(struct lzms_adaptive_state *state, int is_rep) { lzms_update_state(&state->delta_state, is_rep, LZMS_NUM_DELTA_PROBS); } static forceinline void lzms_update_delta_rep_states(struct lzms_adaptive_state *state, int rep_idx) { for (int i = 0; i < rep_idx; i++) lzms_update_state(&state->delta_rep_states[i], 1, LZMS_NUM_DELTA_REP_PROBS); if (rep_idx < LZMS_NUM_DELTA_REP_DECISIONS) lzms_update_state(&state->delta_rep_states[rep_idx], 0, LZMS_NUM_DELTA_REP_PROBS); } /****************************************************************************** * Matchfinding * ******************************************************************************/ /* Note: this code just handles finding delta matches. The code for finding LZ * matches is elsewhere. */ /* Initialize the delta matchfinder for a new input buffer. */ static void lzms_init_delta_matchfinder(struct lzms_compressor *c) { /* Set all entries to use an invalid power, which will never match. */ STATIC_ASSERT(NUM_POWERS_TO_CONSIDER < (1 << (32 - DELTA_SOURCE_POWER_SHIFT))); memset(c->delta_hash_table, 0xFF, sizeof(c->delta_hash_table)); /* Initialize the next hash code for each power. We can just use zeroes * initially; it doesn't really matter. */ for (u32 i = 0; i < NUM_POWERS_TO_CONSIDER; i++) c->next_delta_hashes[i] = 0; } /* * Compute a DELTA_HASH_ORDER-bit hash code for the first * NBYTES_HASHED_FOR_DELTA bytes of the sequence beginning at @p when taken in a * delta context with the specified @span. */ static forceinline u32 lzms_delta_hash(const u8 *p, const u32 pos, u32 span) { /* A delta match has a certain span and an offset that is a multiple of * that span. To reduce wasted space we use a single combined hash * table for all spans and positions, but to minimize collisions we * include in the hash code computation the span and the low-order bits * of the current position. */ STATIC_ASSERT(NBYTES_HASHED_FOR_DELTA == 3); u8 d0 = *(p + 0) - *(p + 0 - span); u8 d1 = *(p + 1) - *(p + 1 - span); u8 d2 = *(p + 2) - *(p + 2 - span); u32 v = ((span + (pos & (span - 1))) << 24) | ((u32)d2 << 16) | ((u32)d1 << 8) | d0; return lz_hash(v, DELTA_HASH_ORDER); } /* * Given a match between @in_next and @matchptr in a delta context with the * specified @span and having the initial @len, extend the match as far as * possible, up to a limit of @max_len. */ static forceinline u32 lzms_extend_delta_match(const u8 *in_next, const u8 *matchptr, u32 len, u32 max_len, u32 span) { while (len < max_len && (u8)(*(in_next + len) - *(in_next + len - span)) == (u8)(*(matchptr + len) - *(matchptr + len - span))) { len++; } return len; } static void lzms_delta_matchfinder_skip_bytes(struct lzms_compressor *c, const u8 *in_next, u32 count) { u32 pos = in_next - c->in_buffer; if (unlikely(c->in_nbytes - (pos + count) <= NBYTES_HASHED_FOR_DELTA + 1)) return; do { /* Update the hash table for each power. */ for (u32 power = 0; power < NUM_POWERS_TO_CONSIDER; power++) { const u32 span = (u32)1 << power; if (unlikely(pos < span)) continue; const u32 next_hash = lzms_delta_hash(in_next + 1, pos + 1, span); const u32 hash = c->next_delta_hashes[power]; c->delta_hash_table[hash] = (power << DELTA_SOURCE_POWER_SHIFT) | pos; c->next_delta_hashes[power] = next_hash; prefetchw(&c->delta_hash_table[next_hash]); } } while (in_next++, pos++, --count); } /* * Skip the next @count bytes (don't search for matches at them). @in_next * points to the first byte to skip. The return value is @in_next + count. */ static const u8 * lzms_skip_bytes(struct lzms_compressor *c, u32 count, const u8 *in_next) { lcpit_matchfinder_skip_bytes(&c->mf, count); if (c->use_delta_matches) lzms_delta_matchfinder_skip_bytes(c, in_next, count); return in_next + count; } /****************************************************************************** * "Near-optimal" parsing * ******************************************************************************/ /* * The main near-optimal parsing routine. * * Briefly, the algorithm does an approximate minimum-cost path search to find a * "near-optimal" sequence of matches and literals to output, based on the * current cost model. The algorithm steps forward, position by position (byte * by byte), and updates the minimum cost path to reach each later position that * can be reached using a match or literal from the current position. This is * essentially Dijkstra's algorithm in disguise: the graph nodes are positions, * the graph edges are possible matches/literals to code, and the cost of each * edge is the estimated number of bits (scaled up by COST_SHIFT) that will be * required to output the corresponding match or literal. But one difference is * that we actually compute the lowest-cost path in pieces, where each piece is * terminated when there are no choices to be made. * * The costs of literals and matches are estimated using the range encoder * states and the semi-adaptive Huffman codes. Except for range encoding * states, costs are assumed to be constant throughout a single run of the * parsing algorithm, which can parse up to NUM_OPTIM_NODES bytes of data. This * introduces a source of non-optimality because the probabilities and Huffman * codes can change over this part of the data. And of course, there are * various other reasons why the result isn't optimal in terms of compression * ratio. */ static void lzms_near_optimal_parse(struct lzms_compressor *c) { const u8 *in_next = c->in_buffer; const u8 * const in_end = &c->in_buffer[c->in_nbytes]; struct lzms_optimum_node *cur_node; struct lzms_optimum_node *end_node; /* Set initial length costs for lengths <= MAX_FAST_LENGTH. */ lzms_update_fast_length_costs(c); /* Set up the initial adaptive state. */ lzms_init_adaptive_state(&c->optimum_nodes[0].state); begin: /* Start building a new list of items, which will correspond to the next * piece of the overall minimum-cost path. */ cur_node = c->optimum_nodes; cur_node->cost = 0; end_node = cur_node; if (in_next == in_end) return; /* The following loop runs once for each per byte in the input buffer, * except in a few shortcut cases. */ for (;;) { u32 num_matches; /* Repeat offset LZ matches */ if (likely(in_next - c->in_buffer >= LZMS_NUM_LZ_REPS && in_end - in_next >= 2)) { for (int rep_idx = 0; rep_idx < LZMS_NUM_LZ_REPS; rep_idx++) { /* Looking for a repeat offset LZ match at queue * index @rep_idx */ const u32 offset = cur_node->state.recent_lz_offsets[rep_idx]; const u8 * const matchptr = in_next - offset; /* Check the first 2 bytes before entering the extension loop. */ if (load_u16_unaligned(in_next) != load_u16_unaligned(matchptr)) continue; /* Extend the match to its full length. */ const u32 rep_len = lz_extend(in_next, matchptr, 2, in_end - in_next); /* Early out for long repeat offset LZ match */ if (rep_len >= c->mf.nice_match_len) { in_next = lzms_skip_bytes(c, rep_len, in_next); lzms_encode_item_list(c, cur_node); lzms_encode_item(c, rep_len, rep_idx); c->optimum_nodes[0].state = cur_node->state; cur_node = &c->optimum_nodes[0]; cur_node->state.upcoming_lz_offset = cur_node->state.recent_lz_offsets[rep_idx]; cur_node->state.upcoming_delta_pair = 0; for (int i = rep_idx; i < LZMS_NUM_LZ_REPS; i++) cur_node->state.recent_lz_offsets[i] = cur_node->state.recent_lz_offsets[i + 1]; lzms_update_lru_queues(&cur_node->state); lzms_update_main_state(&cur_node->state, 1); lzms_update_match_state(&cur_node->state, 0); lzms_update_lz_state(&cur_node->state, 1); lzms_update_lz_rep_states(&cur_node->state, rep_idx); goto begin; } while (end_node < cur_node + rep_len) (++end_node)->cost = INFINITE_COST; u32 base_cost = cur_node->cost + lzms_bit_1_cost(cur_node->state.main_state, c->probs.main) + lzms_bit_0_cost(cur_node->state.match_state, c->probs.match) + lzms_bit_1_cost(cur_node->state.lz_state, c->probs.lz); for (int i = 0; i < rep_idx; i++) base_cost += lzms_bit_1_cost(cur_node->state.lz_rep_states[i], c->probs.lz_rep[i]); if (rep_idx < LZMS_NUM_LZ_REP_DECISIONS) base_cost += lzms_bit_0_cost(cur_node->state.lz_rep_states[rep_idx], c->probs.lz_rep[rep_idx]); u32 len = 2; do { u32 cost = base_cost + lzms_fast_length_cost(c, len); if (cost < (cur_node + len)->cost) { (cur_node + len)->cost = cost; (cur_node + len)->item = (struct lzms_item) { .length = len, .source = rep_idx, }; (cur_node + len)->num_extra_items = 0; } } while (++len <= rep_len); /* try LZ-rep + lit + LZ-rep0 */ if (c->try_lzrep_lit_lzrep0 && in_end - (in_next + rep_len) >= 3 && load_u16_unaligned(in_next + rep_len + 1) == load_u16_unaligned(matchptr + rep_len + 1)) { const u32 rep0_len = lz_extend(in_next + rep_len + 1, matchptr + rep_len + 1, 2, min(c->mf.nice_match_len, in_end - (in_next + rep_len + 1))); unsigned main_state = cur_node->state.main_state; unsigned match_state = cur_node->state.match_state; unsigned lz_state = cur_node->state.lz_state; unsigned lz_rep0_state = cur_node->state.lz_rep_states[0]; /* update states for LZ-rep */ main_state = ((main_state << 1) | 1) % LZMS_NUM_MAIN_PROBS; match_state = ((match_state << 1) | 0) % LZMS_NUM_MATCH_PROBS; lz_state = ((lz_state << 1) | 1) % LZMS_NUM_LZ_PROBS; lz_rep0_state = ((lz_rep0_state << 1) | (rep_idx > 0)) % LZMS_NUM_LZ_REP_PROBS; /* LZ-rep cost */ u32 cost = base_cost + lzms_fast_length_cost(c, rep_len); /* add literal cost */ cost += lzms_literal_cost(c, main_state, *(in_next + rep_len)); /* update state for literal */ main_state = ((main_state << 1) | 0) % LZMS_NUM_MAIN_PROBS; /* add LZ-rep0 cost */ cost += lzms_bit_1_cost(main_state, c->probs.main) + lzms_bit_0_cost(match_state, c->probs.match) + lzms_bit_1_cost(lz_state, c->probs.lz) + lzms_bit_0_cost(lz_rep0_state, c->probs.lz_rep[0]) + lzms_fast_length_cost(c, rep0_len); const u32 total_len = rep_len + 1 + rep0_len; while (end_node < cur_node + total_len) (++end_node)->cost = INFINITE_COST; if (cost < (cur_node + total_len)->cost) { (cur_node + total_len)->cost = cost; (cur_node + total_len)->item = (struct lzms_item) { .length = rep0_len, .source = 0, }; (cur_node + total_len)->extra_items[0] = (struct lzms_item) { .length = 1, .source = *(in_next + rep_len), }; (cur_node + total_len)->extra_items[1] = (struct lzms_item) { .length = rep_len, .source = rep_idx, }; (cur_node + total_len)->num_extra_items = 2; } } } } /* Repeat offset delta matches */ if (c->use_delta_matches && likely(in_next - c->in_buffer >= LZMS_NUM_DELTA_REPS + 1 && (in_end - in_next >= 2))) { for (int rep_idx = 0; rep_idx < LZMS_NUM_DELTA_REPS; rep_idx++) { /* Looking for a repeat offset delta match at * queue index @rep_idx */ const u32 pair = cur_node->state.recent_delta_pairs[rep_idx]; const u32 power = pair >> DELTA_SOURCE_POWER_SHIFT; const u32 raw_offset = pair & DELTA_SOURCE_RAW_OFFSET_MASK; const u32 span = (u32)1 << power; const u32 offset = raw_offset << power; const u8 * const matchptr = in_next - offset; /* Check the first 2 bytes before entering the * extension loop. */ if (((u8)(*(in_next + 0) - *(in_next + 0 - span)) != (u8)(*(matchptr + 0) - *(matchptr + 0 - span))) || ((u8)(*(in_next + 1) - *(in_next + 1 - span)) != (u8)(*(matchptr + 1) - *(matchptr + 1 - span)))) continue; /* Extend the match to its full length. */ const u32 rep_len = lzms_extend_delta_match(in_next, matchptr, 2, in_end - in_next, span); /* Early out for long repeat offset delta match */ if (rep_len >= c->mf.nice_match_len) { in_next = lzms_skip_bytes(c, rep_len, in_next); lzms_encode_item_list(c, cur_node); lzms_encode_item(c, rep_len, DELTA_SOURCE_TAG | rep_idx); c->optimum_nodes[0].state = cur_node->state; cur_node = &c->optimum_nodes[0]; cur_node->state.upcoming_delta_pair = pair; cur_node->state.upcoming_lz_offset = 0; for (int i = rep_idx; i < LZMS_NUM_DELTA_REPS; i++) cur_node->state.recent_delta_pairs[i] = cur_node->state.recent_delta_pairs[i + 1]; lzms_update_lru_queues(&cur_node->state); lzms_update_main_state(&cur_node->state, 1); lzms_update_match_state(&cur_node->state, 1); lzms_update_delta_state(&cur_node->state, 1); lzms_update_delta_rep_states(&cur_node->state, rep_idx); goto begin; } while (end_node < cur_node + rep_len) (++end_node)->cost = INFINITE_COST; u32 base_cost = cur_node->cost + lzms_bit_1_cost(cur_node->state.main_state, c->probs.main) + lzms_bit_1_cost(cur_node->state.match_state, c->probs.match) + lzms_bit_1_cost(cur_node->state.delta_state, c->probs.delta); for (int i = 0; i < rep_idx; i++) base_cost += lzms_bit_1_cost(cur_node->state.delta_rep_states[i], c->probs.delta_rep[i]); if (rep_idx < LZMS_NUM_DELTA_REP_DECISIONS) base_cost += lzms_bit_0_cost(cur_node->state.delta_rep_states[rep_idx], c->probs.delta_rep[rep_idx]); u32 len = 2; do { u32 cost = base_cost + lzms_fast_length_cost(c, len); if (cost < (cur_node + len)->cost) { (cur_node + len)->cost = cost; (cur_node + len)->item = (struct lzms_item) { .length = len, .source = DELTA_SOURCE_TAG | rep_idx, }; (cur_node + len)->num_extra_items = 0; } } while (++len <= rep_len); } } /* Explicit offset LZ matches */ num_matches = lcpit_matchfinder_get_matches(&c->mf, c->matches); if (num_matches) { u32 best_len = c->matches[0].length; /* Early out for long explicit offset LZ match */ if (best_len >= c->mf.nice_match_len) { const u32 offset = c->matches[0].offset; /* Extend the match as far as possible. * This is necessary because the LCP-interval * tree matchfinder only reports up to * nice_match_len bytes. */ best_len = lz_extend(in_next, in_next - offset, best_len, in_end - in_next); in_next = lzms_skip_bytes(c, best_len - 1, in_next + 1); lzms_encode_item_list(c, cur_node); lzms_encode_item(c, best_len, offset + LZMS_NUM_LZ_REPS - 1); c->optimum_nodes[0].state = cur_node->state; cur_node = &c->optimum_nodes[0]; cur_node->state.upcoming_lz_offset = offset; cur_node->state.upcoming_delta_pair = 0; lzms_update_lru_queues(&cur_node->state); lzms_update_main_state(&cur_node->state, 1); lzms_update_match_state(&cur_node->state, 0); lzms_update_lz_state(&cur_node->state, 0); goto begin; } while (end_node < cur_node + best_len) (++end_node)->cost = INFINITE_COST; u32 base_cost = cur_node->cost + lzms_bit_1_cost(cur_node->state.main_state, c->probs.main) + lzms_bit_0_cost(cur_node->state.match_state, c->probs.match) + lzms_bit_0_cost(cur_node->state.lz_state, c->probs.lz); if (c->try_lzmatch_lit_lzrep0 && likely(in_end - (in_next + c->matches[0].length) >= 3)) { /* try LZ-match + lit + LZ-rep0 */ u32 l = 2; u32 i = num_matches - 1; do { const u32 len = c->matches[i].length; const u32 offset = c->matches[i].offset; const u32 position_cost = base_cost + lzms_lz_offset_cost(c, offset); do { u32 cost = position_cost + lzms_fast_length_cost(c, l); if (cost < (cur_node + l)->cost) { (cur_node + l)->cost = cost; (cur_node + l)->item = (struct lzms_item) { .length = l, .source = offset + (LZMS_NUM_LZ_REPS - 1), }; (cur_node + l)->num_extra_items = 0; } } while (++l <= len); const u8 * const matchptr = in_next - offset; if (load_u16_unaligned(matchptr + len + 1) != load_u16_unaligned(in_next + len + 1)) continue; const u32 rep0_len = lz_extend(in_next + len + 1, matchptr + len + 1, 2, min(c->mf.nice_match_len, in_end - (in_next + len + 1))); unsigned main_state = cur_node->state.main_state; unsigned match_state = cur_node->state.match_state; unsigned lz_state = cur_node->state.lz_state; /* update states for LZ-match */ main_state = ((main_state << 1) | 1) % LZMS_NUM_MAIN_PROBS; match_state = ((match_state << 1) | 0) % LZMS_NUM_MATCH_PROBS; lz_state = ((lz_state << 1) | 0) % LZMS_NUM_LZ_PROBS; /* LZ-match cost */ u32 cost = position_cost + lzms_fast_length_cost(c, len); /* add literal cost */ cost += lzms_literal_cost(c, main_state, *(in_next + len)); /* update state for literal */ main_state = ((main_state << 1) | 0) % LZMS_NUM_MAIN_PROBS; /* add LZ-rep0 cost */ cost += lzms_bit_1_cost(main_state, c->probs.main) + lzms_bit_0_cost(match_state, c->probs.match) + lzms_bit_1_cost(lz_state, c->probs.lz) + lzms_bit_0_cost(cur_node->state.lz_rep_states[0], c->probs.lz_rep[0]) + lzms_fast_length_cost(c, rep0_len); const u32 total_len = len + 1 + rep0_len; while (end_node < cur_node + total_len) (++end_node)->cost = INFINITE_COST; if (cost < (cur_node + total_len)->cost) { (cur_node + total_len)->cost = cost; (cur_node + total_len)->item = (struct lzms_item) { .length = rep0_len, .source = 0, }; (cur_node + total_len)->extra_items[0] = (struct lzms_item) { .length = 1, .source = *(in_next + len), }; (cur_node + total_len)->extra_items[1] = (struct lzms_item) { .length = len, .source = offset + LZMS_NUM_LZ_REPS - 1, }; (cur_node + total_len)->num_extra_items = 2; } } while (i--); } else { u32 l = 2; u32 i = num_matches - 1; do { u32 position_cost = base_cost + lzms_lz_offset_cost(c, c->matches[i].offset); do { u32 cost = position_cost + lzms_fast_length_cost(c, l); if (cost < (cur_node + l)->cost) { (cur_node + l)->cost = cost; (cur_node + l)->item = (struct lzms_item) { .length = l, .source = c->matches[i].offset + (LZMS_NUM_LZ_REPS - 1), }; (cur_node + l)->num_extra_items = 0; } } while (++l <= c->matches[i].length); } while (i--); } } /* Explicit offset delta matches */ if (c->use_delta_matches && likely(in_end - in_next >= NBYTES_HASHED_FOR_DELTA + 1)) { const u32 pos = in_next - c->in_buffer; /* Consider each possible power (log2 of span) */ STATIC_ASSERT(NUM_POWERS_TO_CONSIDER <= LZMS_NUM_DELTA_POWER_SYMS); for (u32 power = 0; power < NUM_POWERS_TO_CONSIDER; power++) { const u32 span = (u32)1 << power; if (unlikely(pos < span)) continue; const u32 next_hash = lzms_delta_hash(in_next + 1, pos + 1, span); const u32 hash = c->next_delta_hashes[power]; const u32 cur_match = c->delta_hash_table[hash]; c->delta_hash_table[hash] = (power << DELTA_SOURCE_POWER_SHIFT) | pos; c->next_delta_hashes[power] = next_hash; prefetchw(&c->delta_hash_table[next_hash]); if (power != cur_match >> DELTA_SOURCE_POWER_SHIFT) continue; const u32 offset = pos - (cur_match & DELTA_SOURCE_RAW_OFFSET_MASK); /* The offset must be a multiple of span. */ if (offset & (span - 1)) continue; const u8 * const matchptr = in_next - offset; /* Check the first 3 bytes before entering the * extension loop. */ STATIC_ASSERT(NBYTES_HASHED_FOR_DELTA == 3); if (((u8)(*(in_next + 0) - *(in_next + 0 - span)) != (u8)(*(matchptr + 0) - *(matchptr + 0 - span))) || ((u8)(*(in_next + 1) - *(in_next + 1 - span)) != (u8)(*(matchptr + 1) - *(matchptr + 1 - span))) || ((u8)(*(in_next + 2) - *(in_next + 2 - span)) != (u8)(*(matchptr + 2) - *(matchptr + 2 - span)))) continue; /* Extend the delta match to its full length. */ const u32 len = lzms_extend_delta_match(in_next, matchptr, NBYTES_HASHED_FOR_DELTA, in_end - in_next, span); const u32 raw_offset = offset >> power; if (unlikely(raw_offset > DELTA_SOURCE_RAW_OFFSET_MASK - (LZMS_NUM_DELTA_REPS - 1))) continue; const u32 pair = (power << DELTA_SOURCE_POWER_SHIFT) | raw_offset; const u32 source = DELTA_SOURCE_TAG | (pair + LZMS_NUM_DELTA_REPS - 1); /* Early out for long explicit offset delta match */ if (len >= c->mf.nice_match_len) { in_next = lzms_skip_bytes(c, len - 1, in_next + 1); lzms_encode_item_list(c, cur_node); lzms_encode_item(c, len, source); c->optimum_nodes[0].state = cur_node->state; cur_node = &c->optimum_nodes[0]; cur_node->state.upcoming_lz_offset = 0; cur_node->state.upcoming_delta_pair = pair; lzms_update_lru_queues(&cur_node->state); lzms_update_main_state(&cur_node->state, 1); lzms_update_match_state(&cur_node->state, 1); lzms_update_delta_state(&cur_node->state, 0); goto begin; } while (end_node < cur_node + len) (++end_node)->cost = INFINITE_COST; u32 base_cost = cur_node->cost + lzms_bit_1_cost(cur_node->state.main_state, c->probs.main) + lzms_bit_1_cost(cur_node->state.match_state, c->probs.match) + lzms_bit_0_cost(cur_node->state.delta_state, c->probs.delta) + lzms_delta_source_cost(c, power, raw_offset); u32 l = NBYTES_HASHED_FOR_DELTA; do { u32 cost = base_cost + lzms_fast_length_cost(c, l); if (cost < (cur_node + l)->cost) { (cur_node + l)->cost = cost; (cur_node + l)->item = (struct lzms_item) { .length = l, .source = source, }; (cur_node + l)->num_extra_items = 0; } } while (++l <= len); } } /* Literal */ if (end_node < cur_node + 1) (++end_node)->cost = INFINITE_COST; const u32 cur_and_lit_cost = cur_node->cost + lzms_literal_cost(c, cur_node->state.main_state, *in_next); if (cur_and_lit_cost < (cur_node + 1)->cost) { (cur_node + 1)->cost = cur_and_lit_cost; (cur_node + 1)->item = (struct lzms_item) { .length = 1, .source = *in_next, }; (cur_node + 1)->num_extra_items = 0; } else if (c->try_lit_lzrep0 && in_end - (in_next + 1) >= 2) { /* try lit + LZ-rep0 */ const u32 offset = (cur_node->state.prev_lz_offset) ? cur_node->state.prev_lz_offset : cur_node->state.recent_lz_offsets[0]; if (load_u16_unaligned(in_next + 1) == load_u16_unaligned(in_next + 1 - offset)) { const u32 rep0_len = lz_extend(in_next + 1, in_next + 1 - offset, 2, min(in_end - (in_next + 1), c->mf.nice_match_len)); unsigned main_state = cur_node->state.main_state; /* Update main_state after literal */ main_state = (main_state << 1 | 0) % LZMS_NUM_MAIN_PROBS; /* Add cost of LZ-rep0 */ const u32 cost = cur_and_lit_cost + lzms_bit_1_cost(main_state, c->probs.main) + lzms_bit_0_cost(cur_node->state.match_state, c->probs.match) + lzms_bit_1_cost(cur_node->state.lz_state, c->probs.lz) + lzms_bit_0_cost(cur_node->state.lz_rep_states[0], c->probs.lz_rep[0]) + lzms_fast_length_cost(c, rep0_len); const u32 total_len = 1 + rep0_len; while (end_node < cur_node + total_len) (++end_node)->cost = INFINITE_COST; if (cost < (cur_node + total_len)->cost) { (cur_node + total_len)->cost = cost; (cur_node + total_len)->item = (struct lzms_item) { .length = rep0_len, .source = 0, }; (cur_node + total_len)->extra_items[0] = (struct lzms_item) { .length = 1, .source = *in_next, }; (cur_node + total_len)->num_extra_items = 1; } } } /* Advance to the next position. */ in_next++; cur_node++; /* The lowest-cost path to the current position is now known. * Finalize the adaptive state that results from taking this * lowest-cost path. */ struct lzms_item item_to_take = cur_node->item; struct lzms_optimum_node *source_node = cur_node - item_to_take.length; int next_item_idx = -1; for (unsigned i = 0; i < cur_node->num_extra_items; i++) { item_to_take = cur_node->extra_items[i]; source_node -= item_to_take.length; next_item_idx++; } cur_node->state = source_node->state; for (;;) { const u32 length = item_to_take.length; u32 source = item_to_take.source; cur_node->state.upcoming_lz_offset = 0; cur_node->state.upcoming_delta_pair = 0; if (length > 1) { /* Match */ lzms_update_main_state(&cur_node->state, 1); if (source & DELTA_SOURCE_TAG) { /* Delta match */ lzms_update_match_state(&cur_node->state, 1); source &= ~DELTA_SOURCE_TAG; if (source >= LZMS_NUM_DELTA_REPS) { /* Explicit offset delta match */ lzms_update_delta_state(&cur_node->state, 0); cur_node->state.upcoming_delta_pair = source - (LZMS_NUM_DELTA_REPS - 1); } else { /* Repeat offset delta match */ int rep_idx = source; lzms_update_delta_state(&cur_node->state, 1); lzms_update_delta_rep_states(&cur_node->state, rep_idx); cur_node->state.upcoming_delta_pair = cur_node->state.recent_delta_pairs[rep_idx]; for (int i = rep_idx; i < LZMS_NUM_DELTA_REPS; i++) cur_node->state.recent_delta_pairs[i] = cur_node->state.recent_delta_pairs[i + 1]; } } else { lzms_update_match_state(&cur_node->state, 0); if (source >= LZMS_NUM_LZ_REPS) { /* Explicit offset LZ match */ lzms_update_lz_state(&cur_node->state, 0); cur_node->state.upcoming_lz_offset = source - (LZMS_NUM_LZ_REPS - 1); } else { /* Repeat offset LZ match */ int rep_idx = source; lzms_update_lz_state(&cur_node->state, 1); lzms_update_lz_rep_states(&cur_node->state, rep_idx); cur_node->state.upcoming_lz_offset = cur_node->state.recent_lz_offsets[rep_idx]; for (int i = rep_idx; i < LZMS_NUM_LZ_REPS; i++) cur_node->state.recent_lz_offsets[i] = cur_node->state.recent_lz_offsets[i + 1]; } } } else { /* Literal */ lzms_update_main_state(&cur_node->state, 0); } lzms_update_lru_queues(&cur_node->state); if (next_item_idx < 0) break; if (next_item_idx == 0) item_to_take = cur_node->item; else item_to_take = cur_node->extra_items[next_item_idx - 1]; --next_item_idx; } /* * This loop will terminate when either of the following * conditions is true: * * (1) cur_node == end_node * * There are no paths that extend beyond the current * position. In this case, any path to a later position * must pass through the current position, so we can go * ahead and choose the list of items that led to this * position. * * (2) cur_node == &c->optimum_nodes[NUM_OPTIM_NODES] * * This bounds the number of times the algorithm can step * forward before it is guaranteed to start choosing items. * This limits the memory usage. It also guarantees that * the parser will not go too long without updating the * probability tables. * * Note: no check for end-of-buffer is needed because * end-of-buffer will trigger condition (1). */ if (cur_node == end_node || cur_node == &c->optimum_nodes[NUM_OPTIM_NODES]) { lzms_encode_nonempty_item_list(c, cur_node); c->optimum_nodes[0].state = cur_node->state; goto begin; } } } static void lzms_init_states_and_probabilities(struct lzms_compressor *c) { c->main_state = 0; c->match_state = 0; c->lz_state = 0; for (int i = 0; i < LZMS_NUM_LZ_REP_DECISIONS; i++) c->lz_rep_states[i] = 0; c->delta_state = 0; for (int i = 0; i < LZMS_NUM_DELTA_REP_DECISIONS; i++) c->delta_rep_states[i] = 0; lzms_init_probabilities(&c->probs); } static void lzms_init_huffman_codes(struct lzms_compressor *c, unsigned num_offset_slots) { lzms_init_huffman_code(&c->literal_rebuild_info, LZMS_NUM_LITERAL_SYMS, LZMS_LITERAL_CODE_REBUILD_FREQ, c->literal_codewords, c->literal_lens, c->literal_freqs); lzms_init_huffman_code(&c->lz_offset_rebuild_info, num_offset_slots, LZMS_LZ_OFFSET_CODE_REBUILD_FREQ, c->lz_offset_codewords, c->lz_offset_lens, c->lz_offset_freqs); lzms_init_huffman_code(&c->length_rebuild_info, LZMS_NUM_LENGTH_SYMS, LZMS_LENGTH_CODE_REBUILD_FREQ, c->length_codewords, c->length_lens, c->length_freqs); lzms_init_huffman_code(&c->delta_offset_rebuild_info, num_offset_slots, LZMS_DELTA_OFFSET_CODE_REBUILD_FREQ, c->delta_offset_codewords, c->delta_offset_lens, c->delta_offset_freqs); lzms_init_huffman_code(&c->delta_power_rebuild_info, LZMS_NUM_DELTA_POWER_SYMS, LZMS_DELTA_POWER_CODE_REBUILD_FREQ, c->delta_power_codewords, c->delta_power_lens, c->delta_power_freqs); } /* * Flush the output streams, prepare the final compressed data, and return its * size in bytes. * * A return value of 0 indicates that the data could not be compressed to fit in * the available space. */ static size_t lzms_finalize(struct lzms_compressor *c) { size_t num_forwards_bytes; size_t num_backwards_bytes; /* Flush both the forwards and backwards streams, and make sure they * didn't cross each other and start overwriting each other's data. */ if (!lzms_output_bitstream_flush(&c->os)) return 0; if (!lzms_range_encoder_flush(&c->rc)) return 0; if (c->rc.next > c->os.next) return 0; /* Now the compressed buffer contains the data output by the forwards * bitstream, then empty space, then data output by the backwards * bitstream. Move the data output by the backwards bitstream to be * adjacent to the data output by the forward bitstream, and calculate * the compressed size that this results in. */ num_forwards_bytes = c->rc.next - c->rc.begin; num_backwards_bytes = c->rc.end - c->os.next; memmove(c->rc.next, c->os.next, num_backwards_bytes); return num_forwards_bytes + num_backwards_bytes; } static u64 lzms_get_needed_memory(size_t max_bufsize, unsigned compression_level, bool destructive) { u64 size = 0; if (max_bufsize > LZMS_MAX_BUFFER_SIZE) return 0; size += sizeof(struct lzms_compressor); if (!destructive) size += max_bufsize; /* in_buffer */ /* mf */ size += lcpit_matchfinder_get_needed_memory(max_bufsize); return size; } static int lzms_create_compressor(size_t max_bufsize, unsigned compression_level, bool destructive, void **c_ret) { struct lzms_compressor *c; u32 nice_match_len; if (max_bufsize > LZMS_MAX_BUFFER_SIZE) return WIMLIB_ERR_INVALID_PARAM; c = ALIGNED_MALLOC(sizeof(struct lzms_compressor), 64); if (!c) goto oom0; c->destructive = destructive; /* Scale nice_match_len with the compression level. But to allow an * optimization for length cost calculations, don't allow nice_match_len * to exceed MAX_FAST_LENGTH. */ nice_match_len = min(((u64)compression_level * 63) / 50, MAX_FAST_LENGTH); c->use_delta_matches = (compression_level >= 35); c->try_lzmatch_lit_lzrep0 = (compression_level >= 45); c->try_lit_lzrep0 = (compression_level >= 60); c->try_lzrep_lit_lzrep0 = (compression_level >= 60); if (!c->destructive) { c->in_buffer = MALLOC(max_bufsize); if (!c->in_buffer) goto oom1; } if (!lcpit_matchfinder_init(&c->mf, max_bufsize, 2, nice_match_len)) goto oom2; lzms_init_fast_length_slot_tab(c); lzms_init_offset_slot_tabs(c); *c_ret = c; return 0; oom2: if (!c->destructive) FREE(c->in_buffer); oom1: ALIGNED_FREE(c); oom0: return WIMLIB_ERR_NOMEM; } static size_t lzms_compress(const void *restrict in, size_t in_nbytes, void *restrict out, size_t out_nbytes_avail, void *restrict _c) { struct lzms_compressor *c = _c; size_t result; /* Don't bother trying to compress extremely small inputs. */ if (in_nbytes < 4) return 0; /* Copy the input data into the internal buffer and preprocess it. */ if (c->destructive) c->in_buffer = (void *)in; else memcpy(c->in_buffer, in, in_nbytes); c->in_nbytes = in_nbytes; lzms_x86_filter(c->in_buffer, in_nbytes, c->last_target_usages, false); /* Prepare the matchfinders. */ lcpit_matchfinder_load_buffer(&c->mf, c->in_buffer, c->in_nbytes); if (c->use_delta_matches) lzms_init_delta_matchfinder(c); /* Initialize the encoder structures. */ lzms_range_encoder_init(&c->rc, out, out_nbytes_avail); lzms_output_bitstream_init(&c->os, out, out_nbytes_avail); lzms_init_states_and_probabilities(c); lzms_init_huffman_codes(c, lzms_get_num_offset_slots(c->in_nbytes)); /* The main loop: parse and encode. */ lzms_near_optimal_parse(c); /* Return the compressed data size or 0. */ result = lzms_finalize(c); if (!result && c->destructive) lzms_x86_filter(c->in_buffer, c->in_nbytes, c->last_target_usages, true); return result; } static void lzms_free_compressor(void *_c) { struct lzms_compressor *c = _c; if (!c->destructive) FREE(c->in_buffer); lcpit_matchfinder_destroy(&c->mf); ALIGNED_FREE(c); } const struct compressor_ops lzms_compressor_ops = { .get_needed_memory = lzms_get_needed_memory, .create_compressor = lzms_create_compressor, .compress = lzms_compress, .free_compressor = lzms_free_compressor, }; wimlib-1.13.1/src/decompress_common.c0000644000175000017500000003135513160354224014457 00000000000000/* * decompress_common.c * * Code for decompression shared among multiple compression formats. * * The following copying information applies to this specific source code file: * * Written in 2012-2016 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #ifdef __SSE2__ # include #endif #include "wimlib/decompress_common.h" /* * make_huffman_decode_table() - * * Given an alphabet of symbols and the length of each symbol's codeword in a * canonical prefix code, build a table for quickly decoding symbols that were * encoded with that code. * * A _prefix code_ is an assignment of bitstrings called _codewords_ to symbols * such that no whole codeword is a prefix of any other. A prefix code might be * a _Huffman code_, which means that it is an optimum prefix code for a given * list of symbol frequencies and was generated by the Huffman algorithm. * Although the prefix codes processed here will ordinarily be "Huffman codes", * strictly speaking the decoder cannot know whether a given code was actually * generated by the Huffman algorithm or not. * * A prefix code is _canonical_ if and only if a longer codeword never * lexicographically precedes a shorter codeword, and the lexicographic ordering * of codewords of equal length is the same as the lexicographic ordering of the * corresponding symbols. The advantage of using a canonical prefix code is * that the codewords can be reconstructed from only the symbol => codeword * length mapping. This eliminates the need to transmit the codewords * explicitly. Instead, they can be enumerated in lexicographic order after * sorting the symbols primarily by increasing codeword length and secondarily * by increasing symbol value. * * However, the decoder's real goal is to decode symbols with the code, not just * generate the list of codewords. Consequently, this function directly builds * a table for efficiently decoding symbols using the code. The basic idea is * that given the next 'max_codeword_len' bits of input, the decoder can look up * the next decoded symbol by indexing a table containing '2^max_codeword_len' * entries. A codeword with length 'max_codeword_len' will have exactly one * entry in this table, whereas a codeword shorter than 'max_codeword_len' will * have multiple entries in this table. Precisely, a codeword of length 'n' * will have '2^(max_codeword_len - n)' entries. The index of each such entry, * considered as a bitstring of length 'max_codeword_len', will contain the * corresponding codeword as a prefix. * * That's the basic idea, but we extend it in two ways: * * - Often the maximum codeword length is too long for it to be efficient to * build the full decode table whenever a new code is used. Instead, we build * a "root" table using only '2^table_bits' entries, where 'table_bits <= * max_codeword_len'. Then, a lookup of 'table_bits' bits produces either a * symbol directly (for codewords not longer than 'table_bits'), or the index * of a subtable which must be indexed with additional bits of input to fully * decode the symbol (for codewords longer than 'table_bits'). * * - Whenever the decoder decodes a symbol, it needs to know the codeword length * so that it can remove the appropriate number of input bits. The obvious * solution would be to simply retain the codeword lengths array and use the * decoded symbol as an index into it. However, that would require two array * accesses when decoding each symbol. Our strategy is to instead store the * codeword length directly in the decode table entry along with the symbol. * * See MAKE_DECODE_TABLE_ENTRY() for full details on the format of decode table * entries, and see read_huffsym() for full details on how symbols are decoded. * * @decode_table: * The array in which to build the decode table. This must have been * declared by the DECODE_TABLE() macro. This may alias @lens, since all * @lens are consumed before the decode table is written to. * * @num_syms: * The number of symbols in the alphabet. * * @table_bits: * The log base 2 of the number of entries in the root table. * * @lens: * An array of length @num_syms, indexed by symbol, that gives the length * of the codeword, in bits, for each symbol. The length can be 0, which * means that the symbol does not have a codeword assigned. In addition, * @lens may alias @decode_table, as noted above. * * @max_codeword_len: * The maximum codeword length permitted for this code. All entries in * 'lens' must be less than or equal to this value. * * @working_space * A temporary array that was declared with DECODE_TABLE_WORKING_SPACE(). * * Returns 0 on success, or -1 if the lengths do not form a valid prefix code. */ int make_huffman_decode_table(u16 decode_table[], unsigned num_syms, unsigned table_bits, const u8 lens[], unsigned max_codeword_len, u16 working_space[]) { u16 * const len_counts = &working_space[0]; u16 * const offsets = &working_space[1 * (max_codeword_len + 1)]; u16 * const sorted_syms = &working_space[2 * (max_codeword_len + 1)]; s32 remainder = 1; void *entry_ptr = decode_table; unsigned codeword_len = 1; unsigned sym_idx; unsigned codeword; unsigned subtable_pos; unsigned subtable_bits; unsigned subtable_prefix; /* Count how many codewords have each length, including 0. */ for (unsigned len = 0; len <= max_codeword_len; len++) len_counts[len] = 0; for (unsigned sym = 0; sym < num_syms; sym++) len_counts[lens[sym]]++; /* It is already guaranteed that all lengths are <= max_codeword_len, * but it cannot be assumed they form a complete prefix code. A * codeword of length n should require a proportion of the codespace * equaling (1/2)^n. The code is complete if and only if, by this * measure, the codespace is exactly filled by the lengths. */ for (unsigned len = 1; len <= max_codeword_len; len++) { remainder = (remainder << 1) - len_counts[len]; /* Do the lengths overflow the codespace? */ if (unlikely(remainder < 0)) return -1; } if (remainder != 0) { /* The lengths do not fill the codespace; that is, they form an * incomplete code. This is permitted only if the code is empty * (contains no symbols). */ if (unlikely(remainder != 1U << max_codeword_len)) return -1; /* The code is empty. When processing a well-formed stream, the * decode table need not be initialized in this case. However, * we cannot assume the stream is well-formed, so we must * initialize the decode table anyway. Setting all entries to 0 * makes the decode table always produce symbol '0' without * consuming any bits, which is good enough. */ memset(decode_table, 0, sizeof(decode_table[0]) << table_bits); return 0; } /* Sort the symbols primarily by increasing codeword length and * secondarily by increasing symbol value. */ /* Initialize 'offsets' so that 'offsets[len]' is the number of * codewords shorter than 'len' bits, including length 0. */ offsets[0] = 0; for (unsigned len = 0; len < max_codeword_len; len++) offsets[len + 1] = offsets[len] + len_counts[len]; /* Use the 'offsets' array to sort the symbols. */ for (unsigned sym = 0; sym < num_syms; sym++) sorted_syms[offsets[lens[sym]]++] = sym; /* * Fill the root table entries for codewords no longer than table_bits. * * The table will start with entries for the shortest codeword(s), which * will have the most entries. From there, the number of entries per * codeword will decrease. As an optimization, we may begin filling * entries with SSE2 vector accesses (8 entries/store), then change to * word accesses (2 or 4 entries/store), then change to 16-bit accesses * (1 entry/store). */ sym_idx = offsets[0]; #ifdef __SSE2__ /* Fill entries one 128-bit vector (8 entries) at a time. */ for (unsigned stores_per_loop = (1U << (table_bits - codeword_len)) / (sizeof(__m128i) / sizeof(decode_table[0])); stores_per_loop != 0; codeword_len++, stores_per_loop >>= 1) { unsigned end_sym_idx = sym_idx + len_counts[codeword_len]; for (; sym_idx < end_sym_idx; sym_idx++) { /* Note: unlike in the "word" version below, the __m128i * type already has __attribute__((may_alias)), so using * it to access an array of u16 will not violate strict * aliasing. */ __m128i v = _mm_set1_epi16( MAKE_DECODE_TABLE_ENTRY(sorted_syms[sym_idx], codeword_len)); unsigned n = stores_per_loop; do { *(__m128i *)entry_ptr = v; entry_ptr += sizeof(v); } while (--n); } } #endif /* __SSE2__ */ #ifdef __GNUC__ /* Fill entries one word (2 or 4 entries) at a time. */ for (unsigned stores_per_loop = (1U << (table_bits - codeword_len)) / (WORDBYTES / sizeof(decode_table[0])); stores_per_loop != 0; codeword_len++, stores_per_loop >>= 1) { unsigned end_sym_idx = sym_idx + len_counts[codeword_len]; for (; sym_idx < end_sym_idx; sym_idx++) { /* Accessing the array of u16 as u32 or u64 would * violate strict aliasing and would require compiling * the code with -fno-strict-aliasing to guarantee * correctness. To work around this problem, use the * gcc 'may_alias' extension. */ typedef machine_word_t __attribute__((may_alias)) aliased_word_t; aliased_word_t v = repeat_u16( MAKE_DECODE_TABLE_ENTRY(sorted_syms[sym_idx], codeword_len)); unsigned n = stores_per_loop; do { *(aliased_word_t *)entry_ptr = v; entry_ptr += sizeof(v); } while (--n); } } #endif /* __GNUC__ */ /* Fill entries one at a time. */ for (unsigned stores_per_loop = (1U << (table_bits - codeword_len)); stores_per_loop != 0; codeword_len++, stores_per_loop >>= 1) { unsigned end_sym_idx = sym_idx + len_counts[codeword_len]; for (; sym_idx < end_sym_idx; sym_idx++) { u16 v = MAKE_DECODE_TABLE_ENTRY(sorted_syms[sym_idx], codeword_len); unsigned n = stores_per_loop; do { *(u16 *)entry_ptr = v; entry_ptr += sizeof(v); } while (--n); } } /* If all symbols were processed, then no subtables are required. */ if (sym_idx == num_syms) return 0; /* At least one subtable is required. Process the remaining symbols. */ codeword = ((u16 *)entry_ptr - decode_table) << 1; subtable_pos = 1U << table_bits; subtable_bits = table_bits; subtable_prefix = -1; do { while (len_counts[codeword_len] == 0) { codeword_len++; codeword <<= 1; } unsigned prefix = codeword >> (codeword_len - table_bits); /* Start a new subtable if the first 'table_bits' bits of the * codeword don't match the prefix for the previous subtable, or * if this will be the first subtable. */ if (prefix != subtable_prefix) { subtable_prefix = prefix; /* * Calculate the subtable length. If the codeword * length exceeds 'table_bits' by n, then the subtable * needs at least 2^n entries. But it may need more; if * there are fewer than 2^n codewords of length * 'table_bits + n' remaining, then n will need to be * incremented to bring in longer codewords until the * subtable can be filled completely. Note that it * always will, eventually, be possible to fill the * subtable, since it was previously verified that the * code is complete. */ subtable_bits = codeword_len - table_bits; remainder = (s32)1 << subtable_bits; for (;;) { remainder -= len_counts[table_bits + subtable_bits]; if (remainder <= 0) break; subtable_bits++; remainder <<= 1; } /* Create the entry that points from the root table to * the subtable. This entry contains the index of the * start of the subtable and the number of bits with * which the subtable is indexed (the log base 2 of the * number of entries it contains). */ decode_table[subtable_prefix] = MAKE_DECODE_TABLE_ENTRY(subtable_pos, subtable_bits); } /* Fill the subtable entries for this symbol. */ u16 entry = MAKE_DECODE_TABLE_ENTRY(sorted_syms[sym_idx], codeword_len - table_bits); unsigned n = 1U << (subtable_bits - (codeword_len - table_bits)); do { decode_table[subtable_pos++] = entry; } while (--n); len_counts[codeword_len]--; codeword++; } while (++sym_idx < num_syms); return 0; } wimlib-1.13.1/src/win32_vss.c0000644000175000017500000004452213160354225012601 00000000000000/* * win32_vss.c - Windows-specific code for creating VSS (Volume Shadow Copy * Service) snapshots. */ /* * Copyright (C) 2015-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef __WIN32__ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "wimlib/win32_common.h" #include #include #include "wimlib/error.h" #include "wimlib/util.h" #include "wimlib/win32_vss.h" /*----------------------------------------------------------------------------* * VSS API declarations * *----------------------------------------------------------------------------*/ typedef GUID VSS_ID; typedef LONGLONG VSS_TIMESTAMP; typedef WCHAR *VSS_PWSZ; typedef enum { VSS_BT_UNDEFINED = 0, VSS_BT_FULL = 1, VSS_BT_INCREMENTAL = 2, VSS_BT_DIFFERENTIAL = 3, VSS_BT_LOG = 4, VSS_BT_COPY = 5, VSS_BT_OTHER = 6, } VSS_BACKUP_TYPE; typedef enum { VSS_SS_UNKNOWN = 0x00, VSS_SS_PREPARING = 0x01, VSS_SS_PROCESSING_PREPARE = 0x02, VSS_SS_PREPARED = 0x03, VSS_SS_PROCESSING_PRECOMMIT = 0x04, VSS_SS_PRECOMMITTED = 0x05, VSS_SS_PROCESSING_COMMIT = 0x06, VSS_SS_COMMITTED = 0x07, VSS_SS_PROCESSING_POSTCOMMIT = 0x08, VSS_SS_PROCESSING_PREFINALCOMMIT = 0x09, VSS_SS_PREFINALCOMMITTED = 0x0a, VSS_SS_PROCESSING_POSTFINALCOMMIT = 0x0b, VSS_SS_CREATED = 0x0c, VSS_SS_ABORTED = 0x0d, VSS_SS_DELETED = 0x0e, VSS_SS_POSTCOMMITTED = 0x0f, VSS_SS_COUNT = 0x10, } VSS_SNAPSHOT_STATE; typedef enum { VSS_VOLSNAP_ATTR_PERSISTENT = 0x00000001, VSS_VOLSNAP_ATTR_NO_AUTORECOVERY = 0x00000002, VSS_VOLSNAP_ATTR_CLIENT_ACCESSIBLE = 0x00000004, VSS_VOLSNAP_ATTR_NO_AUTO_RELEASE = 0x00000008, VSS_VOLSNAP_ATTR_NO_WRITERS = 0x00000010, VSS_VOLSNAP_ATTR_TRANSPORTABLE = 0x00000020, VSS_VOLSNAP_ATTR_NOT_SURFACED = 0x00000040, VSS_VOLSNAP_ATTR_NOT_TRANSACTED = 0x00000080, VSS_VOLSNAP_ATTR_HARDWARE_ASSISTED = 0x00010000, VSS_VOLSNAP_ATTR_DIFFERENTIAL = 0x00020000, VSS_VOLSNAP_ATTR_PLEX = 0x00040000, VSS_VOLSNAP_ATTR_IMPORTED = 0x00080000, VSS_VOLSNAP_ATTR_EXPOSED_LOCALLY = 0x00100000, VSS_VOLSNAP_ATTR_EXPOSED_REMOTELY = 0x00200000, VSS_VOLSNAP_ATTR_AUTORECOVER = 0x00400000, VSS_VOLSNAP_ATTR_ROLLBACK_RECOVERY = 0x00800000, VSS_VOLSNAP_ATTR_DELAYED_POSTSNAPSHOT = 0x01000000, VSS_VOLSNAP_ATTR_TXF_RECOVERY = 0x02000000, } VSS_VOLUME_SNAPSHOT_ATTRIBUTES; typedef enum { VSS_CTX_BACKUP = 0, VSS_CTX_FILE_SHARE_BACKUP = VSS_VOLSNAP_ATTR_NO_WRITERS, VSS_CTX_NAS_ROLLBACK = ( ( VSS_VOLSNAP_ATTR_PERSISTENT | VSS_VOLSNAP_ATTR_NO_AUTO_RELEASE ) | VSS_VOLSNAP_ATTR_NO_WRITERS ), VSS_CTX_APP_ROLLBACK = ( VSS_VOLSNAP_ATTR_PERSISTENT | VSS_VOLSNAP_ATTR_NO_AUTO_RELEASE ), VSS_CTX_CLIENT_ACCESSIBLE = ( ( ( VSS_VOLSNAP_ATTR_PERSISTENT | VSS_VOLSNAP_ATTR_CLIENT_ACCESSIBLE ) | VSS_VOLSNAP_ATTR_NO_AUTO_RELEASE ) | VSS_VOLSNAP_ATTR_NO_WRITERS ), VSS_CTX_CLIENT_ACCESSIBLE_WRITERS = ( ( VSS_VOLSNAP_ATTR_PERSISTENT | VSS_VOLSNAP_ATTR_CLIENT_ACCESSIBLE ) | VSS_VOLSNAP_ATTR_NO_AUTO_RELEASE ), VSS_CTX_ALL = 0xffffffff, } VSS_SNAPSHOT_CONTEXT; typedef struct { VSS_ID m_SnapshotId; VSS_ID m_SnapshotSetId; LONG m_lSnapshotsCount; VSS_PWSZ m_pwszSnapshotDeviceObject; VSS_PWSZ m_pwszOriginalVolumeName; VSS_PWSZ m_pwszOriginatingMachine; VSS_PWSZ m_pwszServiceMachine; VSS_PWSZ m_pwszExposedName; VSS_PWSZ m_pwszExposedPath; VSS_ID m_ProviderId; LONG m_lSnapshotAttributes; VSS_TIMESTAMP m_tsCreationTimestamp; VSS_SNAPSHOT_STATE m_eStatus; } VSS_SNAPSHOT_PROP; typedef struct IVssAsyncVTable IVssAsyncVTable; typedef struct { IVssAsyncVTable *vtable; } IVssAsync; struct IVssAsyncVTable { void *QueryInterface; void *AddRef; ULONG (WINAPI *Release)(IVssAsync *this); void *Cancel; union { HRESULT (WINAPI *Wait)(IVssAsync *this, DWORD dwMilliseconds); /* Pre-Vista version */ HRESULT (WINAPI *OldWait)(IVssAsync *this); }; void *QueryStatus; }; typedef struct IVssBackupComponentsVTable IVssBackupComponentsVTable; typedef struct IVssBackupComponentsVTable_old IVssBackupComponentsVTable_old; typedef union { IVssBackupComponentsVTable *vtable; IVssBackupComponentsVTable_old *old_vtable; } IVssBackupComponents; struct IVssBackupComponentsVTable { void *QueryInterface; void *AddRef; ULONG (WINAPI *Release)(IVssBackupComponents *this); void *GetWriterComponentsCount; void *GetWriterComponents; HRESULT (WINAPI *InitializeForBackup)(IVssBackupComponents *this, BSTR bstrXML); HRESULT (WINAPI *SetBackupState)(IVssBackupComponents *this, BOOLEAN bSelectComponents, BOOLEAN bBackupBootableSystemState, VSS_BACKUP_TYPE backupType, BOOLEAN bPartialFileSupport); void *InitializeForRestore; void *SetRestoreState; HRESULT (WINAPI *GatherWriterMetadata)(IVssBackupComponents *this, IVssAsync **ppAsync); void *GetWriterMetadataCount; void *GetWriterMetadata; void *FreeWriterMetadata; void *AddComponent; HRESULT (WINAPI *PrepareForBackup)(IVssBackupComponents *this, IVssAsync **ppAsync); void *AbortBackup; void *GatherWriterStatus; void *GetWriterStatusCount; void *FreeWriterStatus; void *GetWriterStatus; void *SetBackupSucceeded; void *SetBackupOptions; void *SetSelectedForRestore; void *SetRestoreOptions; void *SetAdditionalRestores; void *SetPreviousBackupStamp; void *SaveAsXML; void *BackupComplete; void *AddAlternativeLocationMapping; void *AddRestoreSubcomponent; void *SetFileRestoreStatus; void *AddNewTarget; void *SetRangesFilePath; void *PreRestore; void *PostRestore; HRESULT (WINAPI *SetContext)(IVssBackupComponents *this, LONG lContext); HRESULT (WINAPI *StartSnapshotSet)(IVssBackupComponents *this, VSS_ID *pSnapshotSetId); HRESULT (WINAPI *AddToSnapshotSet)(IVssBackupComponents *this, VSS_PWSZ pwszVolumeName, VSS_ID ProviderId, VSS_ID *pidSnapshot); HRESULT (WINAPI *DoSnapshotSet)(IVssBackupComponents *this, IVssAsync **ppAsync); void *DeleteSnapshots; void *ImportSnapshots; /*void *RemountReadWrite;*/ /* Old API only */ void *BreakSnapshotSet; HRESULT (WINAPI *GetSnapshotProperties)(IVssBackupComponents *this, VSS_ID SnapshotId, VSS_SNAPSHOT_PROP *pprop); void *Query; void *IsVolumeSupported; void *DisableWriterClasses; void *EnableWriterClasses; void *DisableWriterInstances; void *ExposeSnapshot; void *RevertToSnapshot; void *QueryRevertStatus; }; /* Pre-Vista version */ struct IVssBackupComponentsVTable_old { void *QueryInterface; void *AddRef; ULONG (WINAPI *Release)(IVssBackupComponents *this); void *GetWriterComponentsCount; void *GetWriterComponents; HRESULT (WINAPI *InitializeForBackup)(IVssBackupComponents *this, BSTR bstrXML); HRESULT (WINAPI *SetBackupState)(IVssBackupComponents *this, BOOLEAN bSelectComponents, BOOLEAN bBackupBootableSystemState, VSS_BACKUP_TYPE backupType, BOOLEAN bPartialFileSupport); void *InitializeForRestore; /*void *SetRestoreState;*/ /* New API only */ HRESULT (WINAPI *GatherWriterMetadata)(IVssBackupComponents *this, IVssAsync **ppAsync); void *GetWriterMetadataCount; void *GetWriterMetadata; void *FreeWriterMetadata; void *AddComponent; HRESULT (WINAPI *PrepareForBackup)(IVssBackupComponents *this, IVssAsync **ppAsync); void *AbortBackup; void *GatherWriterStatus; void *GetWriterStatusCount; void *FreeWriterStatus; void *GetWriterStatus; void *SetBackupSucceeded; void *SetBackupOptions; void *SetSelectedForRestore; void *SetRestoreOptions; void *SetAdditionalRestores; void *SetPreviousBackupStamp; void *SaveAsXML; void *BackupComplete; void *AddAlternativeLocationMapping; void *AddRestoreSubcomponent; void *SetFileRestoreStatus; /*void *AddNewTarget;*/ /* New API only */ /*void *SetRangesFilePath;*/ /* New API only */ void *PreRestore; void *PostRestore; HRESULT (WINAPI *SetContext)(IVssBackupComponents *this, LONG lContext); HRESULT (WINAPI *StartSnapshotSet)(IVssBackupComponents *this, VSS_ID *pSnapshotSetId); HRESULT (WINAPI *AddToSnapshotSet)(IVssBackupComponents *this, VSS_PWSZ pwszVolumeName, VSS_ID ProviderId, VSS_ID *pidSnapshot); HRESULT (WINAPI *DoSnapshotSet)(IVssBackupComponents *this, IVssAsync **ppAsync); void *DeleteSnapshots; void *ImportSnapshots; void *RemountReadWrite; void *BreakSnapshotSet; HRESULT (WINAPI *GetSnapshotProperties)(IVssBackupComponents *this, VSS_ID SnapshotId, VSS_SNAPSHOT_PROP *pprop); void *Query; void *IsVolumeSupported; void *DisableWriterClasses; void *EnableWriterClasses; void *DisableWriterInstances; void *ExposeSnapshot; /*void *RevertToSnapshot;*/ /* New API only */ /*void *QueryRevertStatus;*/ /* New API only */ }; /* Call a method, assuming its signature is identical in the old and new APIs */ #define CALL_METHOD(obj, method, ...) \ ({ \ HRESULT res; \ if (is_old_api) \ res = (obj)->old_vtable->method((obj), ##__VA_ARGS__); \ else \ res = (obj)->vtable->method((obj), ##__VA_ARGS__); \ res; \ }) /*----------------------------------------------------------------------------* * VSS API initialization * *----------------------------------------------------------------------------*/ static bool vss_initialized; static pthread_mutex_t vss_initialization_mutex = PTHREAD_MUTEX_INITIALIZER; /* vssapi.dll */ static bool is_old_api; /* old VSS API (pre-Vista)? */ static HANDLE hVssapi; static HRESULT (WINAPI *func_CreateVssBackupComponents)(IVssBackupComponents **ppBackup); static void (WINAPI *func_VssFreeSnapshotProperties)(VSS_SNAPSHOT_PROP *pProp); /* ole32.dll */ static HANDLE hOle32; static void (WINAPI *func_CoInitialize)(LPVOID *pvReserved); static void (WINAPI *func_CoUninitialize)(void); static bool vss_global_init_impl(void) { hVssapi = LoadLibrary(L"vssapi.dll"); if (!hVssapi) { ERROR("vssapi.dll not found"); goto err; } func_CreateVssBackupComponents = (void *)GetProcAddress(hVssapi, "CreateVssBackupComponentsInternal"); if (!func_CreateVssBackupComponents) { func_CreateVssBackupComponents = (void *)GetProcAddress(hVssapi, "?CreateVssBackupComponents@@YGJPAPAVIVssBackupComponents@@@Z"); if (!func_CreateVssBackupComponents) { ERROR("CreateVssBackupComponents() not found in vssapi.dll"); goto err_vssapi; } is_old_api = true; } else { is_old_api = false; } func_VssFreeSnapshotProperties = (void *)GetProcAddress(hVssapi, "VssFreeSnapshotPropertiesInternal"); if (!func_VssFreeSnapshotProperties) { func_VssFreeSnapshotProperties = (void *)GetProcAddress(hVssapi, "VssFreeSnapshotProperties"); if (!func_VssFreeSnapshotProperties) { ERROR("VssFreeSnapshotProperties() not found in vssapi.dll"); goto err_vssapi; } } hOle32 = LoadLibrary(L"ole32.dll"); if (!hOle32) { ERROR("ole32.dll not found"); goto err_vssapi; } func_CoInitialize = (void *)GetProcAddress(hOle32, "CoInitialize"); if (!func_CoInitialize) { ERROR("CoInitialize() not found in ole32.dll"); goto err_ole32; } func_CoUninitialize = (void *)GetProcAddress(hOle32, "CoUninitialize"); if (!func_CoUninitialize) { ERROR("CoUninitialize() not found in ole32.dll"); goto err_ole32; } (*func_CoInitialize)(NULL); return true; err_ole32: FreeLibrary(hOle32); err_vssapi: FreeLibrary(hVssapi); err: return false; } static bool vss_global_init(void) { if (vss_initialized) return true; pthread_mutex_lock(&vss_initialization_mutex); if (!vss_initialized) vss_initialized = vss_global_init_impl(); pthread_mutex_unlock(&vss_initialization_mutex); if (vss_initialized) return true; ERROR("The Volume Shadow Copy Service (VSS) API could not be " "initialized. Probably it isn't supported on this computer."); return false; } void vss_global_cleanup(void) { if (!vss_initialized) return; pthread_mutex_lock(&vss_initialization_mutex); if (vss_initialized) { (*func_CoUninitialize)(); FreeLibrary(hOle32); FreeLibrary(hVssapi); vss_initialized = false; } pthread_mutex_unlock(&vss_initialization_mutex); } /*----------------------------------------------------------------------------* * VSS implementation * *----------------------------------------------------------------------------*/ struct vss_snapshot_internal { struct vss_snapshot base; IVssBackupComponents *vss; VSS_SNAPSHOT_PROP props; }; /* Delete the specified VSS snapshot. */ void vss_delete_snapshot(struct vss_snapshot *snapshot) { struct vss_snapshot_internal *internal; internal = container_of(snapshot, struct vss_snapshot_internal, base); if (internal->props.m_pwszSnapshotDeviceObject) (*func_VssFreeSnapshotProperties)(&internal->props); if (internal->vss) CALL_METHOD(internal->vss, Release); FREE(internal); } static HRESULT wait_and_release(IVssAsync *async) { HRESULT res; if (is_old_api) res = async->vtable->OldWait(async); else res = async->vtable->Wait(async, INFINITE); async->vtable->Release(async); return res; } static bool request_vss_snapshot(IVssBackupComponents *vss, wchar_t *volume, VSS_ID *snapshot_id) { HRESULT res; IVssAsync *async; res = CALL_METHOD(vss, InitializeForBackup, NULL); if (FAILED(res)) { ERROR("IVssBackupComponents.InitializeForBackup() error: %x", res); return false; } res = CALL_METHOD(vss, SetBackupState, FALSE, TRUE, VSS_BT_COPY, FALSE); if (FAILED(res)) { ERROR("IVssBackupComponents.SetBackupState() error: %x", res); return false; } res = CALL_METHOD(vss, StartSnapshotSet, snapshot_id); if (FAILED(res)) { ERROR("IVssBackupComponents.StartSnapshotSet() error: %x", res); return false; } res = CALL_METHOD(vss, AddToSnapshotSet, volume, (GUID){}, snapshot_id); if (FAILED(res)) { ERROR("IVssBackupComponents.AddToSnapshotSet() error: %x", res); return false; } res = CALL_METHOD(vss, PrepareForBackup, &async); if (FAILED(res)) { ERROR("IVssBackupComponents.PrepareForBackup() error: %x", res); return false; } res = wait_and_release(async); if (FAILED(res)) { ERROR("IVssAsync.Wait() error while preparing for backup: %x", res); return false; } res = CALL_METHOD(vss, DoSnapshotSet, &async); if (FAILED(res)) { ERROR("IVssBackupComponents.DoSnapshotSet() error: %x", res); return false; } res = wait_and_release(async); if (FAILED(res)) { ERROR("IVssAsync.Wait() error while doing snapshot set: %x", res); return false; } return true; } static bool is_wow64(void) { BOOL wow64 = FALSE; if (sizeof(size_t) == 4) IsWow64Process(GetCurrentProcess(), &wow64); return wow64; } /* * Create a VSS snapshot of the specified @volume. Return the NT namespace path * to the snapshot root directory in @vss_path_ret and a handle to the snapshot * in @snapshot_ret. */ int vss_create_snapshot(const wchar_t *source, UNICODE_STRING *vss_path_ret, struct vss_snapshot **snapshot_ret) { wchar_t *source_abspath; wchar_t volume[4]; VSS_ID snapshot_id; struct vss_snapshot_internal *snapshot = NULL; IVssBackupComponents *vss; HRESULT res; int ret; source_abspath = realpath(source, NULL); if (!source_abspath) { ret = WIMLIB_ERR_NOMEM; goto err; } if (source_abspath[0] == L'\0' || source_abspath[1] != L':' || source_abspath[2] != L'\\') { ERROR("\"%ls\" (full path \"%ls\"): Path format not recognized", source, source_abspath); ret = WIMLIB_ERR_UNSUPPORTED; goto err; } wsprintf(volume, L"%lc:\\", source_abspath[0]); snapshot = CALLOC(1, sizeof(*snapshot)); if (!snapshot) { ret = WIMLIB_ERR_NOMEM; goto err; } if (!vss_global_init()) goto vss_err; res = (*func_CreateVssBackupComponents)(&vss); if (FAILED(res)) { ERROR("CreateVssBackupComponents error: %x", res); goto vss_err; } snapshot->vss = vss; if (!request_vss_snapshot(vss, volume, &snapshot_id)) goto vss_err; res = CALL_METHOD(vss, GetSnapshotProperties, snapshot_id, &snapshot->props); if (FAILED(res)) { ERROR("IVssBackupComponents.GetSnapshotProperties() error: %x", res); goto vss_err; } if (wcsncmp(snapshot->props.m_pwszSnapshotDeviceObject, L"\\\\?\\", 4)) { ERROR("Unexpected volume shadow device path: %ls", snapshot->props.m_pwszSnapshotDeviceObject); goto vss_err; } vss_path_ret->MaximumLength = sizeof(wchar_t) * (wcslen(snapshot->props.m_pwszSnapshotDeviceObject) + 1 + wcslen(&source_abspath[3]) + 1); vss_path_ret->Length = vss_path_ret->MaximumLength - sizeof(wchar_t); vss_path_ret->Buffer = HeapAlloc(GetProcessHeap(), 0, vss_path_ret->MaximumLength); if (!vss_path_ret->Buffer) { ret = WIMLIB_ERR_NOMEM; goto err; } wsprintf(vss_path_ret->Buffer, L"\\??\\%ls\\%ls", &snapshot->props.m_pwszSnapshotDeviceObject[4], &source_abspath[3]); *snapshot_ret = &snapshot->base; snapshot->base.refcnt = 1; ret = 0; goto out; vss_err: ret = WIMLIB_ERR_SNAPSHOT_FAILURE; if (is_wow64()) { ERROR("64-bit Windows doesn't allow 32-bit applications to " "create VSS snapshots.\n" " Run the 64-bit version of this application " "instead."); } else { ERROR("A problem occurred while creating a VSS snapshot of " "\"%ls\".\n" " Aborting the operation.", volume); } err: if (snapshot) vss_delete_snapshot(&snapshot->base); out: FREE(source_abspath); return ret; } #endif /* __WIN32__ */ wimlib-1.13.1/src/unix_capture.c0000644000175000017500000004374713272231267013466 00000000000000/* * unix_capture.c: Capture a directory tree on UNIX. */ /* * Copyright (C) 2012-2018 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifndef __WIN32__ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #ifdef HAVE_SYS_XATTR_H # include #endif #include #include "wimlib/blob_table.h" #include "wimlib/dentry.h" #include "wimlib/error.h" #include "wimlib/reparse.h" #include "wimlib/scan.h" #include "wimlib/timestamp.h" #include "wimlib/unix_data.h" #include "wimlib/xattr.h" #ifdef HAVE_FDOPENDIR # define my_fdopendir(dirfd_p) fdopendir(*(dirfd_p)) #else static DIR * my_fdopendir(int *dirfd_p) { DIR *dir = NULL; int old_pwd; old_pwd = open(".", O_RDONLY); if (old_pwd >= 0) { if (!fchdir(*dirfd_p)) { dir = opendir("."); if (dir) { close(*dirfd_p); *dirfd_p = dirfd(dir); } fchdir(old_pwd); } close(old_pwd); } return dir; } #endif #ifdef HAVE_OPENAT # define my_openat(full_path, dirfd, relpath, flags) \ openat((dirfd), (relpath), (flags)) #else # define my_openat(full_path, dirfd, relpath, flags) \ open((full_path), (flags)) #endif #ifdef HAVE_READLINKAT # define my_readlinkat(full_path, dirfd, relpath, buf, bufsize) \ readlinkat((dirfd), (relpath), (buf), (bufsize)) #else # define my_readlinkat(full_path, dirfd, relpath, buf, bufsize) \ readlink((full_path), (buf), (bufsize)) #endif #ifdef HAVE_FSTATAT # define my_fstatat(full_path, dirfd, relpath, stbuf, flags) \ fstatat((dirfd), (relpath), (stbuf), (flags)) #else # define my_fstatat(full_path, dirfd, relpath, stbuf, flags) \ ((flags) & AT_SYMLINK_NOFOLLOW) ? \ lstat((full_path), (stbuf)) : \ stat((full_path), (stbuf)) #endif #ifndef AT_FDCWD # define AT_FDCWD -100 #endif #ifndef AT_SYMLINK_NOFOLLOW # define AT_SYMLINK_NOFOLLOW 0x100 #endif #ifdef HAVE_LINUX_XATTR_SUPPORT /* * Retrieves the values of the xattrs named by the null-terminated @names of the * file at @path and serializes the xattr names and values into @entries. If * successful, returns the number of bytes used in @entries. If unsuccessful, * returns -1 and sets errno (ERANGE if @entries was too small). */ static ssize_t gather_xattr_entries(const char *path, const char *names, size_t names_size, void *entries, size_t entries_size) { const char * const names_end = names + names_size; void * const entries_end = entries + entries_size; const char *name = names; struct wim_xattr_entry *entry = entries; do { size_t name_len = strnlen(name, names_end - name); void *value; ssize_t value_len; if (name_len == 0 || name_len >= names_end - name) { ERROR("\"%s\": malformed extended attribute names list", path); errno = EINVAL; return -1; } if (name_len > WIM_XATTR_NAME_MAX) { WARNING("\"%s\": name of extended attribute \"%s\" is too long to store", path, name); goto next_name; } /* * Take care to always call lgetxattr() with a nonzero size, * since zero size means to return the value length only. */ if (entries_end - (void *)entry <= sizeof(*entry) + name_len + 1) { errno = ERANGE; return -1; } entry->name_len = name_len; entry->flags = 0; value = mempcpy(entry->name, name, name_len + 1); value_len = lgetxattr(path, name, value, entries_end - value); if (value_len < 0) { if (errno != ERANGE) { ERROR_WITH_ERRNO("\"%s\": unable to read extended attribute \"%s\"", path, name); } return -1; } if (value_len > WIM_XATTR_SIZE_MAX) { WARNING("\"%s\": value of extended attribute \"%s\" is too large to store", path, name); goto next_name; } entry->value_len = cpu_to_le16(value_len); entry = value + value_len; next_name: name += name_len + 1; } while (name < names_end); return (void *)entry - entries; } static int create_xattr_item(const char *path, struct wim_inode *inode, const char *names, size_t names_size) { char _entries[1024]; char *entries = _entries; size_t entries_avail = ARRAY_LEN(_entries); ssize_t entries_size; int ret; retry: /* Serialize the xattrs into @entries */ entries_size = gather_xattr_entries(path, names, names_size, entries, entries_avail); if (entries_size < 0) { ret = WIMLIB_ERR_STAT; if (errno != ERANGE) goto out; /* Not enough space in @entries. Reallocate it. */ if (entries != _entries) FREE(entries); ret = WIMLIB_ERR_NOMEM; entries_avail *= 2; entries = MALLOC(entries_avail); if (!entries) goto out; goto retry; } /* Copy @entries into an xattr item associated with @inode */ if ((u32)entries_size != entries_size) { ERROR("\"%s\": too much xattr data!", path); ret = WIMLIB_ERR_STAT; goto out; } ret = WIMLIB_ERR_NOMEM; if (!inode_set_xattrs(inode, entries, entries_size)) goto out; ret = 0; out: if (entries != _entries) FREE(entries); return ret; } /* * If the file at @path has Linux-style extended attributes, read them into * memory and add them to @inode as a tagged item. */ static noinline_for_stack int scan_linux_xattrs(const char *path, struct wim_inode *inode) { char _names[256]; char *names = _names; ssize_t names_size = ARRAY_LEN(_names); int ret = 0; retry: /* Gather the names of the xattrs of the file at @path */ names_size = llistxattr(path, names, names_size); if (names_size == 0) /* No xattrs? */ goto out; if (names_size < 0) { /* xattrs unsupported or disabled? */ if (errno == ENOTSUP || errno == ENOSYS) goto out; if (errno == ERANGE) { /* * Not enough space in @names. Ask for how much space * we need, then try again. */ names_size = llistxattr(path, NULL, 0); if (names_size == 0) goto out; if (names_size > 0) { if (names != _names) FREE(names); names = MALLOC(names_size); if (!names) { ret = WIMLIB_ERR_NOMEM; goto out; } goto retry; } } /* Some other error occurred. */ ERROR_WITH_ERRNO("\"%s\": unable to list extended attributes", path); ret = WIMLIB_ERR_STAT; goto out; } /* * We have a nonempty list of xattr names. Gather the xattr values and * add them as a tagged item. */ ret = create_xattr_item(path, inode, names, names_size); out: if (names != _names) FREE(names); return ret; } #endif /* HAVE_LINUX_XATTR_SUPPORT */ static int unix_scan_regular_file(const char *path, u64 blocks, u64 size, struct wim_inode *inode, struct list_head *unhashed_blobs) { struct blob_descriptor *blob = NULL; struct wim_inode_stream *strm; /* * Set FILE_ATTRIBUTE_SPARSE_FILE if the file uses less disk space than * expected given its size. */ if (blocks < DIV_ROUND_UP(size, 512)) inode->i_attributes = FILE_ATTRIBUTE_SPARSE_FILE; else inode->i_attributes = FILE_ATTRIBUTE_NORMAL; if (size) { blob = new_blob_descriptor(); if (unlikely(!blob)) goto err_nomem; blob->file_on_disk = STRDUP(path); if (unlikely(!blob->file_on_disk)) goto err_nomem; blob->blob_location = BLOB_IN_FILE_ON_DISK; blob->size = size; blob->file_inode = inode; } strm = inode_add_stream(inode, STREAM_TYPE_DATA, NO_STREAM_NAME, blob); if (unlikely(!strm)) goto err_nomem; prepare_unhashed_blob(blob, inode, strm->stream_id, unhashed_blobs); return 0; err_nomem: free_blob_descriptor(blob); return WIMLIB_ERR_NOMEM; } static int unix_build_dentry_tree_recursive(struct wim_dentry **tree_ret, int dirfd, const char *relpath, struct scan_params *params); static int unix_scan_directory(struct wim_dentry *dir_dentry, int parent_dirfd, const char *dir_relpath, struct scan_params *params) { int dirfd; DIR *dir; int ret; dirfd = my_openat(params->cur_path, parent_dirfd, dir_relpath, O_RDONLY); if (dirfd < 0) { ERROR_WITH_ERRNO("\"%s\": Can't open directory", params->cur_path); return WIMLIB_ERR_OPENDIR; } dir_dentry->d_inode->i_attributes = FILE_ATTRIBUTE_DIRECTORY; dir = my_fdopendir(&dirfd); if (!dir) { ERROR_WITH_ERRNO("\"%s\": Can't open directory", params->cur_path); close(dirfd); return WIMLIB_ERR_OPENDIR; } ret = 0; for (;;) { struct dirent *entry; struct wim_dentry *child; size_t name_len; size_t orig_path_len; errno = 0; entry = readdir(dir); if (!entry) { if (errno) { ret = WIMLIB_ERR_READ; ERROR_WITH_ERRNO("\"%s\": Error reading directory", params->cur_path); } break; } name_len = strlen(entry->d_name); if (should_ignore_filename(entry->d_name, name_len)) continue; ret = WIMLIB_ERR_NOMEM; if (!pathbuf_append_name(params, entry->d_name, name_len, &orig_path_len)) break; ret = unix_build_dentry_tree_recursive(&child, dirfd, entry->d_name, params); pathbuf_truncate(params, orig_path_len); if (ret) break; attach_scanned_tree(dir_dentry, child, params->blob_table); } closedir(dir); return ret; } /* * Given an absolute symbolic link target (UNIX-style, beginning with '/'), * determine whether it points into the directory identified by @ino and @dev. * If yes, return the suffix of @target which is relative to this directory, but * retaining leading slashes. If no, return @target. * * Here are some examples, assuming that the @ino/@dev directory is "/home/e": * * Original target New target * --------------- ---------- * /home/e/test /test * /home/e/test/ /test/ * //home//e//test// //test// * /home/e (empty string) * /home/e/ / * /usr/lib /usr/lib (external link) * * Because of the possibility of other links into the @ino/@dev directory and/or * multiple path separators, we can't simply do a string comparison; instead we * need to stat() each ancestor directory. * * If the link points directly to the @ino/@dev directory with no trailing * slashes, then the new target will be an empty string. This is not a valid * UNIX symlink target, but we store this in the archive anyway since the target * is intended to be de-relativized when the link is extracted. */ static char * unix_relativize_link_target(char *target, u64 ino, u64 dev) { char *p = target; do { char save; struct stat stbuf; int ret; /* Skip slashes (guaranteed to be at least one here) */ do { p++; } while (*p == '/'); /* End of string? */ if (!*p) break; /* Skip non-slashes (guaranteed to be at least one here) */ do { p++; } while (*p && *p != '/'); /* Get the inode and device numbers for this prefix. */ save = *p; *p = '\0'; ret = stat(target, &stbuf); *p = save; if (ret) { /* stat() failed. Assume the link points outside the * directory tree being captured. */ break; } if (stbuf.st_ino == ino && stbuf.st_dev == dev) { /* Link points inside directory tree being captured. * Return abbreviated path. */ return p; } } while (*p); /* Link does not point inside directory tree being captured. */ return target; } static noinline_for_stack int unix_scan_symlink(int dirfd, const char *relpath, struct wim_inode *inode, struct scan_params *params) { char orig_target[REPARSE_POINT_MAX_SIZE]; char *target = orig_target; int ret; /* Read the UNIX symbolic link target. */ ret = my_readlinkat(params->cur_path, dirfd, relpath, target, sizeof(orig_target)); if (unlikely(ret < 0)) { ERROR_WITH_ERRNO("\"%s\": Can't read target of symbolic link", params->cur_path); return WIMLIB_ERR_READLINK; } if (unlikely(ret >= sizeof(orig_target))) { ERROR("\"%s\": target of symbolic link is too long", params->cur_path); return WIMLIB_ERR_READLINK; } target[ret] = '\0'; /* If the link is absolute and reparse point fixups are enabled, then * change it to be "absolute" relative to the tree being captured. */ if (target[0] == '/' && (params->add_flags & WIMLIB_ADD_FLAG_RPFIX)) { int status = WIMLIB_SCAN_DENTRY_NOT_FIXED_SYMLINK; params->progress.scan.symlink_target = target; target = unix_relativize_link_target(target, params->capture_root_ino, params->capture_root_dev); if (target != orig_target) { /* Link target was fixed. */ inode->i_rp_flags &= ~WIM_RP_FLAG_NOT_FIXED; status = WIMLIB_SCAN_DENTRY_FIXED_SYMLINK; } ret = do_scan_progress(params, status, NULL); if (ret) return ret; } /* Translate the UNIX symlink target into a Windows reparse point. */ ret = wim_inode_set_symlink(inode, target, params->blob_table); if (unlikely(ret)) { if (ret == WIMLIB_ERR_INVALID_UTF8_STRING) { ERROR("\"%s\": target of symbolic link is not valid " "UTF-8. This is not supported.", params->cur_path); } return ret; } /* On Windows, a reparse point can be set on both directory and * non-directory files. Usually, a link that is intended to point to a * (non-)directory is stored as a reparse point on a (non-)directory * file. Replicate this behavior by examining the target file. */ struct stat stbuf; if (my_fstatat(params->cur_path, dirfd, relpath, &stbuf, 0) == 0 && S_ISDIR(stbuf.st_mode)) inode->i_attributes |= FILE_ATTRIBUTE_DIRECTORY; return 0; } static int unix_build_dentry_tree_recursive(struct wim_dentry **tree_ret, int dirfd, const char *relpath, struct scan_params *params) { struct wim_dentry *tree = NULL; struct wim_inode *inode = NULL; int ret; struct stat stbuf; int stat_flags; ret = try_exclude(params); if (unlikely(ret < 0)) /* Excluded? */ goto out_progress; if (unlikely(ret > 0)) /* Error? */ goto out; if (params->add_flags & (WIMLIB_ADD_FLAG_DEREFERENCE | WIMLIB_ADD_FLAG_ROOT)) stat_flags = 0; else stat_flags = AT_SYMLINK_NOFOLLOW; ret = my_fstatat(params->cur_path, dirfd, relpath, &stbuf, stat_flags); if (ret) { ERROR_WITH_ERRNO("\"%s\": Can't read metadata", params->cur_path); ret = WIMLIB_ERR_STAT; goto out; } if (!(params->add_flags & WIMLIB_ADD_FLAG_UNIX_DATA)) { if (unlikely(!S_ISREG(stbuf.st_mode) && !S_ISDIR(stbuf.st_mode) && !S_ISLNK(stbuf.st_mode))) { if (params->add_flags & WIMLIB_ADD_FLAG_NO_UNSUPPORTED_EXCLUDE) { ERROR("\"%s\": File type is unsupported", params->cur_path); ret = WIMLIB_ERR_UNSUPPORTED_FILE; goto out; } ret = do_scan_progress(params, WIMLIB_SCAN_DENTRY_UNSUPPORTED, NULL); goto out; } } ret = inode_table_new_dentry(params->inode_table, relpath, stbuf.st_ino, stbuf.st_dev, false, &tree); if (unlikely(ret)) { if (ret == WIMLIB_ERR_INVALID_UTF8_STRING) { ERROR("\"%s\": filename is not valid UTF-8. " "This is not supported.", params->cur_path); } goto out; } inode = tree->d_inode; /* Already seen this inode? */ if (inode->i_nlink > 1) goto out_progress; #ifdef HAVE_STAT_NANOSECOND_PRECISION inode->i_creation_time = timespec_to_wim_timestamp(&stbuf.st_mtim); inode->i_last_write_time = timespec_to_wim_timestamp(&stbuf.st_mtim); inode->i_last_access_time = timespec_to_wim_timestamp(&stbuf.st_atim); #else inode->i_creation_time = time_t_to_wim_timestamp(stbuf.st_mtime); inode->i_last_write_time = time_t_to_wim_timestamp(stbuf.st_mtime); inode->i_last_access_time = time_t_to_wim_timestamp(stbuf.st_atime); #endif if (params->add_flags & WIMLIB_ADD_FLAG_UNIX_DATA) { struct wimlib_unix_data unix_data; unix_data.uid = stbuf.st_uid; unix_data.gid = stbuf.st_gid; unix_data.mode = stbuf.st_mode; unix_data.rdev = stbuf.st_rdev; if (!inode_set_unix_data(inode, &unix_data, UNIX_DATA_ALL)) { ret = WIMLIB_ERR_NOMEM; goto out; } #ifdef HAVE_LINUX_XATTR_SUPPORT ret = scan_linux_xattrs(params->cur_path, inode); if (ret) goto out; #endif } if (params->add_flags & WIMLIB_ADD_FLAG_ROOT) { params->capture_root_ino = stbuf.st_ino; params->capture_root_dev = stbuf.st_dev; params->add_flags &= ~WIMLIB_ADD_FLAG_ROOT; } if (S_ISREG(stbuf.st_mode)) { ret = unix_scan_regular_file(params->cur_path, stbuf.st_blocks, stbuf.st_size, inode, params->unhashed_blobs); } else if (S_ISDIR(stbuf.st_mode)) { ret = unix_scan_directory(tree, dirfd, relpath, params); } else if (S_ISLNK(stbuf.st_mode)) { ret = unix_scan_symlink(dirfd, relpath, inode, params); } if (ret) goto out; out_progress: if (likely(tree)) ret = do_scan_progress(params, WIMLIB_SCAN_DENTRY_OK, inode); else ret = do_scan_progress(params, WIMLIB_SCAN_DENTRY_EXCLUDED, NULL); out: if (unlikely(ret)) { free_dentry_tree(tree, params->blob_table); tree = NULL; ret = report_scan_error(params, ret); } *tree_ret = tree; return ret; } /* * unix_build_dentry_tree(): * Builds a tree of WIM dentries from an on-disk directory tree (UNIX * version; no NTFS-specific data is captured). * * @root_ret: Place to return a pointer to the root of the dentry tree. Set * to NULL if the file or directory was excluded from capture. * * @root_disk_path: The path to the root of the directory tree on disk. * * @params: See doc for `struct scan_params'. * * @return: 0 on success, nonzero on failure. It is a failure if any of * the files cannot be `stat'ed, or if any of the needed * directories cannot be opened or read. Failure to add the files * to the WIM may still occur later when trying to actually read * the on-disk files during a call to wimlib_write() or * wimlib_overwrite(). */ int unix_build_dentry_tree(struct wim_dentry **root_ret, const char *root_disk_path, struct scan_params *params) { int ret; ret = pathbuf_init(params, root_disk_path); if (ret) return ret; return unix_build_dentry_tree_recursive(root_ret, AT_FDCWD, root_disk_path, params); } #endif /* !__WIN32__ */ wimlib-1.13.1/README.WINDOWS0000644000175000017500000001655613376107253012036 00000000000000 INTRODUCTION wimlib is free and open source software that is available on both UNIX-like systems and Windows. This file provides additional information specifically about the Windows version of wimlib and the command line tool "wimlib-imagex" that is distributed with it. It does not obsolete the generic README.txt, which you should read too. WINDOWS DISTRIBUTION The Windows distribution of wimlib is a ZIP file containing the following items: * wimlib-imagex.exe, a command-line tool to deal with WIM (.wim), split WIM (.swm), and ESD (.esd) files that is inspired by Microsoft's ImageX and DISM. This is a ready-to-run executable and not an installer. * Very short batch scripts (e.g. wimapply.cmd) which are shortcuts to the corresponding wimlib-imagex commands (e.g. `wimlib-imagex apply'). * The library itself in DLL format (libwim-15.dll). wimlib-imagex.exe requires this to run. * The documentation, including this file, the generic README.txt, and PDF documentation for wimlib-imagex in the 'doc' directory. * License files for all software included. These are all free software licenses. COPYING.txt is the main license, and it refers to COPYING.GPLv3.txt and COPYING.LGPLv3.txt. The other licenses are for third-party software included in the library. * Development files in the 'devel' directory. These are only needed if you are developing C or C++ applications that use wimlib. Note that there are separate ZIP files for 32-bit (i686) and 64-bit (x86_64) binaries. They are both fully supported, but you should prefer the 64-bit binaries when possible as they can be noticeably faster. WIMLIB-IMAGEX wimlib-imagex supports most features of Microsoft's ImageX as well as some features that are supported by DISM but not by ImageX. wimlib-imagex also supports some features that neither ImageX nor DISM support. Some of the advantages of wimlib-imagex compared to ImageX and DISM are: * wimlib-imagex provides "extract" and "update" commands which allow you to quickly work with WIM images without mounting them. * wimlib-imagex provides an easy-to-use "optimize" command which removes wasted space from a WIM file and optionally recompresses it with stronger compression. * wimlib includes advanced implementations of all compression algorithms used in WIM files. They usually outperform and outcompress their Microsoft equivalents. * wimlib-imagex supports solid WIM files and LZMS compression, for example as used in ESD (.esd) files. (These are partially supported by recent DISM versions but not by ImageX.) * wimlib-imagex supports imaging a live Windows system. Just use the --snapshot option. * In many cases, wimlib-imagex has simpler command-line syntax than either ImageX or DISM. * Whenever possible, wimlib-imagex includes improved documentation and informational output compared to Microsoft's software. * wimlib and wimlib-imagex are free software, so you can modify and/or audit the source code. However, some limitations of wimlib-imagex compared to ImageX and DISM are: * On Windows, wimlib-imagex does not support mounting WIM images. * wimlib-imagex has no awareness of Windows "packages". ADDITIONAL NOTES It's recommended to use wimlib-imagex in scripts to avoid having to interactively enter commands. However, note that wimlib-imagex is largely just a command-line front-end for wimlib, and it's possible to use wimlib's API in other front-ends or applications. Currently there is no official graphical user interface available for wimlib or wimlib-imagex. However, an unofficial, beta, Windows-only graphical user interface that provides a thin wrapper around wimlib-imagex can be downloaded at http://reboot.pro/files/file/485-wimlib-imagex-command-line-compiler/. BUILDING FROM SOURCE As with other open source software, advanced users may choose to build wimlib from source, potentially with customizations. Although wimlib's build system is designed for UNIX-like systems and is easiest to use on Linux, it's possible to build Windows binaries on Windows using Cygwin with MinGW. To do this, follow the instructions below. For the sake of example, I'll assume you are building a 64-bit version of wimlib v1.13.0. Run the Cygwin installer, available from https://www.cygwin.com/setup-x86.exe. When you get to the package selection screen, choose the following additional packages from category "Devel": - make - mingw64-x86_64-binutils - mingw64-x86_64-gcc-g++ - mingw64-x86_64-libxml2 - mingw64-x86_64-pkg-config - mingw64-x86_64-winpthreads Download wimlib's source code from https://wimlib.net/downloads/wimlib-1.13.0.tar.gz. Start a Cygwin terminal and run the following commands: cd /cygdrive/c/Users/example/Downloads # (or wherever you downloaded the source to) tar xf wimlib-1.13.0.tar.gz cd wimlib-1.13.0 ./configure --host=x86_64-w64-mingw32 make If successful, the new binaries "libwim-15.dll" and "wimlib-imagex.exe" will have been produced in the .libs directory. By default the binaries are built with debug symbols. If desired, you can use x86_64-w64-mingw32-strip to strip them. libwim-15.dll will be linked to several other DLLs which you will need as well: - libwinpthread-1.dll - libxml2-2.dll, which also requires: - iconv.dll - liblzma-5.dll - zlib1.dll These DLLs can be found in "C:\cygwin\usr\x86_64-w64-mingw32\sys-root\mingw\bin" and must be placed alongside libwim-15.dll for it to run portably. But see below for an alternative. Building 32-bit binaries is very similar, but you'll need to replace "x86_64" with "i686" everywhere in the above instructions, and libwim-15.dll will also depend on libgcc_s_sjlj-1.dll. Note that you can build both 32-bit and 64-bit binaries from the same Cygwin installation, provided that you install both the mingw64-i686-* and mingw64-x86_64-* packages; and you can run the Cygwin setup program to install more packages at any time. In the official binary releases from wimlib.net, libwim-15.dll's dependent libraries are linked in statically rather than dynamically, so it does not depend on any DLLs other than standard Windows DLLs. If you want to do this, install the following additional Cygwin packages: - p7zip (category "Archiver") - autoconf (category "Devel") - automake (category "Devel") - git (category "Devel") - libtool (category "Devel") - nasm (category "Devel") - pkg-config (category "Devel") - ghostscript (category "Graphics") - wget (category "Web") Then, in a Cygwin terminal, clone the git repository, checkout the wimlib version you want, bootstrap the repository, and run the Windows release script: git clone git://wimlib.net/wimlib cd wimlib git checkout v1.13.0 ./bootstrap ./tools/make-windows-release x86_64 The release script will download and build libxml2 and winpthreads as static libraries, then build wimlib, then do some final tasks and bundle the resulting files up into a ZIP archive. If successful you'll end up with a file like "wimlib-1.13.0-windows-x86_64-bin.zip", just like the official releases. For 32-bit binaries just use "i686" instead of "x86_64". wimlib-1.13.1/configure0000744000175000017500000175724013464166626011744 00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for wimlib 1.13.1. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1 test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || ( ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO PATH=/empty FPATH=/empty; export PATH FPATH test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\ || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and $0: ebiggers3@gmail.com about your system, including any $0: error possibly output before this message. Then install $0: a modern shell, or manually run the script under such a $0: shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" SHELL=${CONFIG_SHELL-/bin/sh} test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='wimlib' PACKAGE_TARNAME='wimlib' PACKAGE_VERSION='1.13.1' PACKAGE_STRING='wimlib 1.13.1' PACKAGE_BUGREPORT='ebiggers3@gmail.com' PACKAGE_URL='' ac_unique_file="src/wim.c" # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS LIBOBJS PKGCONFIG_PRIVATE_LIBS PKGCONFIG_PRIVATE_REQUIRES ENABLE_TEST_SUPPORT_FALSE ENABLE_TEST_SUPPORT_TRUE pkgconfigdir ENABLE_SSSE3_SHA1_FALSE ENABLE_SSSE3_SHA1_TRUE LIBCRYPTO_LIBS LIBCRYPTO_CFLAGS NASM_SYMBOL_PREFIX NASM_PLATFORM_FLAGS NAFLAGS NASM WITH_FUSE_FALSE WITH_FUSE_TRUE LIBRT_LIBS LIBFUSE_LIBS LIBFUSE_CFLAGS WITH_NTFS_3G_FALSE WITH_NTFS_3G_TRUE LIBNTFS_3G_LIBS LIBNTFS_3G_CFLAGS LIBXML2_LIBS LIBXML2_CFLAGS PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG PTHREAD_CFLAGS PTHREAD_LIBS PTHREAD_CC ax_pthread_config WINDOWS_NATIVE_BUILD_FALSE WINDOWS_NATIVE_BUILD_TRUE PLATFORM_LDFLAGS PLATFORM_CFLAGS PLATFORM_CPPFLAGS LT_SYS_LIBRARY_PATH OTOOL64 OTOOL LIPO NMEDIT DSYMUTIL MANIFEST_TOOL RANLIB DLLTOOL OBJDUMP LN_S NM ac_ct_DUMPBIN DUMPBIN LD FGREP SED host_os host_vendor host_cpu host build_os build_vendor build_cpu build LIBTOOL ac_ct_AR AR EGREP GREP CPP am__fastdepCC_FALSE am__fastdepCC_TRUE CCDEPMODE am__nodep AMDEPBACKSLASH AMDEP_FALSE AMDEP_TRUE am__include DEPDIR OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC AM_BACKSLASH AM_DEFAULT_VERBOSITY AM_DEFAULT_V AM_V am__untar am__tar AMTAR am__leading_dot SET_MAKE AWK mkdir_p MKDIR_P INSTALL_STRIP_PROGRAM STRIP install_sh MAKEINFO AUTOHEADER AUTOMAKE AUTOCONF ACLOCAL VERSION PACKAGE CYGPATH_W am__isrc INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL am__quote' ac_subst_files='' ac_user_opts=' enable_option_checking enable_silent_rules enable_dependency_tracking enable_shared enable_static with_pic enable_fast_install with_aix_soname with_gnu_ld with_sysroot enable_libtool_lock with_ntfs_3g with_fuse enable_ssse3_sha1 with_libcrypto enable_error_messages enable_assertions enable_multithreaded_compression with_pkgconfigdir enable_test_support ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CPP LT_SYS_LIBRARY_PATH PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR LIBXML2_CFLAGS LIBXML2_LIBS LIBNTFS_3G_CFLAGS LIBNTFS_3G_LIBS LIBFUSE_CFLAGS LIBFUSE_LIBS LIBCRYPTO_CFLAGS LIBCRYPTO_LIBS' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures wimlib 1.13.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/wimlib] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF Program names: --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names --program-transform-name=PROGRAM run sed PROGRAM on installed program names System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of wimlib 1.13.1:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-silent-rules less verbose build output (undo: "make V=1") --disable-silent-rules verbose build output (undo: "make V=0") --enable-dependency-tracking do not reject slow dependency extractors --disable-dependency-tracking speeds up one-time build --enable-shared[=PKGS] build shared libraries [default=yes] --enable-static[=PKGS] build static libraries [default=yes] --enable-fast-install[=PKGS] optimize for fast installation [default=yes] --disable-libtool-lock avoid locking (might break parallel builds) --enable-ssse3-sha1 Include SSSE3-accelerated SHA-1 implementation by Intel. This implies --without-libcrypto. --disable-error-messages do not compile in error messsages --disable-assertions do not include assertions --disable-multithreaded-compression disable support for multithreaded compression --enable-test-support Enable supporting code for tests (developers only) Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use both] --with-aix-soname=aix|svr4|both shared library versioning (aka "SONAME") variant to provide on AIX, [default=aix]. --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-sysroot[=DIR] Search for dependent libraries within DIR (or the compiler's sysroot if not specified). --without-ntfs-3g build without libntfs-3g. This will disable the ability to capture or apply a WIM image directly from/to an unmounted NTFS volume. --without-fuse build without libfuse. This will disable the ability to mount WIM images. --without-libcrypto build in the SHA-1 algorithm, rather than use external libcrypto from OpenSSL (default is autodetect) --with-pkgconfigdir=DIR pkgconfig file in DIR [LIBDIR/pkgconfig] Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor LT_SYS_LIBRARY_PATH User-defined run-time library search path. PKG_CONFIG path to pkg-config utility PKG_CONFIG_PATH directories to add to pkg-config's search path PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path LIBXML2_CFLAGS C compiler flags for LIBXML2, overriding pkg-config LIBXML2_LIBS linker flags for LIBXML2, overriding pkg-config LIBNTFS_3G_CFLAGS C compiler flags for LIBNTFS_3G, overriding pkg-config LIBNTFS_3G_LIBS linker flags for LIBNTFS_3G, overriding pkg-config LIBFUSE_CFLAGS C compiler flags for LIBFUSE, overriding pkg-config LIBFUSE_LIBS linker flags for LIBFUSE, overriding pkg-config LIBCRYPTO_CFLAGS C compiler flags for LIBCRYPTO, overriding pkg-config LIBCRYPTO_LIBS linker flags for LIBCRYPTO, overriding pkg-config Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF wimlib configure 1.13.1 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ( $as_echo "## ---------------------------------- ## ## Report this to ebiggers3@gmail.com ## ## ---------------------------------- ##" ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel # ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES # ---------------------------------------------------- # Tries to find if the field MEMBER exists in type AGGR, after including # INCLUDES, setting cache variable VAR accordingly. ac_fn_c_check_member () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 $as_echo_n "checking for $2.$3... " >&6; } if eval \${$4+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $5 int main () { static $2 ac_aggr; if (ac_aggr.$3) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$4=yes" else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $5 int main () { static $2 ac_aggr; if (sizeof ac_aggr.$3) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$4=yes" else eval "$4=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$4 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_member # ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES # --------------------------------------------- # Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR # accordingly. ac_fn_c_check_decl () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack as_decl_name=`echo $2|sed 's/ *(.*//'` as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 $as_echo_n "checking whether $as_decl_name is declared... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { #ifndef $as_decl_name #ifdef __cplusplus (void) $as_decl_use; #else (void) $as_decl_name; #endif #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_decl cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by wimlib $as_me 1.13.1, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_aux_dir= for ac_dir in build-aux "$srcdir"/build-aux; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in build-aux \"$srcdir\"/build-aux" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. am__api_version='1.16' # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if ${ac_cv_path_install+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in #(( ./ | .// | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 $as_echo "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 $as_echo_n "checking whether build environment is sane... " >&6; } # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[\\\"\#\$\&\'\`$am_lf]*) as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; esac case $srcdir in *[\\\"\#\$\&\'\`$am_lf\ \ ]*) as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$*" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$*" != "X $srcdir/configure conftest.file" \ && test "$*" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". as_fn_error $? "ls -t appears to fail. Make sure there is not a broken alias in your environment" "$LINENO" 5 fi if test "$2" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$2" = conftest.file ) then # Ok. : else as_fn_error $? "newly created file is older than distributed files! Check your system clock" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi rm -f conftest.file test "$program_prefix" != NONE && program_transform_name="s&^&$program_prefix&;$program_transform_name" # Use a double $ so make ignores it. test "$program_suffix" != NONE && program_transform_name="s&\$&$program_suffix&;$program_transform_name" # Double any \ or $. # By default was `s,x,x', remove it if useless. ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 $as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} fi if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. if test "$cross_compiling" != no; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 $as_echo_n "checking for a thread-safe mkdir -p... " >&6; } if test -z "$MKDIR_P"; then if ${ac_cv_path_mkdir+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in mkdir gmkdir; do for ac_exec_ext in '' $ac_executable_extensions; do as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( 'mkdir (GNU coreutils) '* | \ 'mkdir (coreutils) '* | \ 'mkdir (fileutils) '4.1*) ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext break 3;; esac done done done IFS=$as_save_IFS fi test -d ./--version && rmdir ./--version if test "${ac_cv_path_mkdir+set}" = set; then MKDIR_P="$ac_cv_path_mkdir -p" else # As a last resort, use the slow shell script. Don't cache a # value for MKDIR_P within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. MKDIR_P="$ac_install_sh -d" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 $as_echo "$MKDIR_P" >&6; } for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AWK+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 $as_echo "$AWK" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AWK" && break done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null # Check whether --enable-silent-rules was given. if test "${enable_silent_rules+set}" = set; then : enableval=$enable_silent_rules; fi case $enable_silent_rules in # ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=1;; esac am_make=${MAKE-make} { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 $as_echo_n "checking whether $am_make supports nested variables... " >&6; } if ${am_cv_make_support_nested_variables+:} false; then : $as_echo_n "(cached) " >&6 else if $as_echo 'TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 $as_echo "$am_cv_make_support_nested_variables" >&6; } if test $am_cv_make_support_nested_variables = yes; then AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AM_BACKSLASH='\' if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." am__isrc=' -I$(srcdir)' # test to see if srcdir already configured if test -f $srcdir/config.status; then as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi # Define the identity of the package. PACKAGE='wimlib' VERSION='1.13.1' cat >>confdefs.h <<_ACEOF #define PACKAGE "$PACKAGE" _ACEOF cat >>confdefs.h <<_ACEOF #define VERSION "$VERSION" _ACEOF # Some tools Automake needs. ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # mkdir_p='$(MKDIR_P)' # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. # Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AMTAR='$${TAR-tar}' # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar pax cpio none' am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 fi fi # Check whether --enable-silent-rules was given. if test "${enable_silent_rules+set}" = set; then : enableval=$enable_silent_rules; fi case $enable_silent_rules in # ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=0;; esac am_make=${MAKE-make} { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 $as_echo_n "checking whether $am_make supports nested variables... " >&6; } if ${am_cv_make_support_nested_variables+:} false; then : $as_echo_n "(cached) " >&6 else if $as_echo 'TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 $as_echo "$am_cv_make_support_nested_variables" >&6; } if test $am_cv_make_support_nested_variables = yes; then AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AM_BACKSLASH='\' DEPDIR="${am__leading_dot}deps" ac_config_commands="$ac_config_commands depfiles" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5 $as_echo_n "checking whether ${MAKE-make} supports the include directive... " >&6; } cat > confinc.mk << 'END' am__doit: @echo this is the am__doit target >confinc.out .PHONY: am__doit END am__include="#" am__quote= # BSD make does it like this. echo '.include "confinc.mk" # ignored' > confmf.BSD # Other make implementations (GNU, Solaris 10, AIX) do it like this. echo 'include confinc.mk # ignored' > confmf.GNU _am_result=no for s in GNU BSD; do { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5 (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } case $?:`cat confinc.out 2>/dev/null` in #( '0:this is the am__doit target') : case $s in #( BSD) : am__include='.include' am__quote='"' ;; #( *) : am__include='include' am__quote='' ;; esac ;; #( *) : ;; esac if test "$am__include" != "#"; then _am_result="yes ($s style)" break fi done rm -f confinc.* confmf.* { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5 $as_echo "${_am_result}" >&6; } # Check whether --enable-dependency-tracking was given. if test "${enable_dependency_tracking+set}" = set; then : enableval=$enable_dependency_tracking; fi if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi if test "x$enable_dependency_tracking" != xno; then AMDEP_TRUE= AMDEP_FALSE='#' else AMDEP_TRUE='#' AMDEP_FALSE= fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 $as_echo_n "checking whether $CC understands -c and -o together... " >&6; } if ${am_cv_prog_cc_c_o+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 $as_echo "$am_cv_prog_cc_c_o" >&6; } if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu depcc="$CC" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if ${am_cv_CC_dependencies_compiler_type+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CC_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CC_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CC_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 $as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then am__fastdepCC_TRUE= am__fastdepCC_FALSE='#' else am__fastdepCC_TRUE='#' am__fastdepCC_FALSE= fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 $as_echo_n "checking whether byte ordering is bigendian... " >&6; } if ${ac_cv_c_bigendian+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_c_bigendian=unknown # See if we're dealing with a universal compiler. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __APPLE_CC__ not a universal capable compiler #endif typedef int dummy; _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # Check for potential -arch flags. It is not universal unless # there are at least two -arch flags with different values. ac_arch= ac_prev= for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do if test -n "$ac_prev"; then case $ac_word in i?86 | x86_64 | ppc | ppc64) if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then ac_arch=$ac_word else ac_cv_c_bigendian=universal break fi ;; esac ac_prev= elif test "x$ac_word" = "x-arch"; then ac_prev=arch fi done fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_c_bigendian = unknown; then # See if sys/param.h defines the BYTE_ORDER macro. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ && LITTLE_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # It does; now see whether it defined to BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if BYTE_ORDER != BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # It does; now see whether it defined to _BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #ifndef _BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # Compile a test program. if test "$cross_compiling" = yes; then : # Try to guess by grepping values from an object file. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; int use_ascii (int i) { return ascii_mm[i] + ascii_ii[i]; } short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; int use_ebcdic (int i) { return ebcdic_mm[i] + ebcdic_ii[i]; } extern int foo; int main () { return use_ascii (foo) == use_ebcdic (foo); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then ac_cv_c_bigendian=yes fi if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else # finding both strings is unlikely to happen, but who knows? ac_cv_c_bigendian=unknown fi fi fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main () { /* Are we little or big endian? From Harbison&Steele. */ union { long int l; char c[sizeof (long int)]; } u; u.l = 1; return u.c[sizeof (long int) - 1] == 1; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ac_cv_c_bigendian=no else ac_cv_c_bigendian=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 $as_echo "$ac_cv_c_bigendian" >&6; } case $ac_cv_c_bigendian in #( yes) $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h ;; #( no) ;; #( universal) $as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h ;; #( *) as_fn_error $? "unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; esac if test -n "$ac_tool_prefix"; then for ac_prog in ar lib "link -lib" do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AR="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AR=$ac_cv_prog_AR if test -n "$AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 $as_echo "$AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AR" && break done fi if test -z "$AR"; then ac_ct_AR=$AR for ac_prog in ar lib "link -lib" do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_AR="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 $as_echo "$ac_ct_AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_AR" && break done if test "x$ac_ct_AR" = x; then AR="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR fi fi : ${AR=ar} { $as_echo "$as_me:${as_lineno-$LINENO}: checking the archiver ($AR) interface" >&5 $as_echo_n "checking the archiver ($AR) interface... " >&6; } if ${am_cv_ar_interface+:} false; then : $as_echo_n "(cached) " >&6 else ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu am_cv_ar_interface=ar cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int some_variable = 0; _ACEOF if ac_fn_c_try_compile "$LINENO"; then : am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&5' { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5 (eval $am_ar_try) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test "$ac_status" -eq 0; then am_cv_ar_interface=ar else am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&5' { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5 (eval $am_ar_try) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test "$ac_status" -eq 0; then am_cv_ar_interface=lib else am_cv_ar_interface=unknown fi fi rm -f conftest.lib libconftest.a fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_ar_interface" >&5 $as_echo "$am_cv_ar_interface" >&6; } case $am_cv_ar_interface in ar) ;; lib) # Microsoft lib, so override with the ar-lib wrapper script. # FIXME: It is wrong to rewrite AR. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__AR in this case, # and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something # similar. AR="$am_aux_dir/ar-lib $AR" ;; unknown) as_fn_error $? "could not determine $AR interface" "$LINENO" 5 ;; esac case `pwd` in *\ * | *\ *) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 $as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; esac macro_version='2.4.6.42-b88ce' macro_revision='2.4.6.42' ltmain=$ac_aux_dir/ltmain.sh # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 $as_echo_n "checking build system type... " >&6; } if ${ac_cv_build+:} false; then : $as_echo_n "(cached) " >&6 else ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 $as_echo "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 $as_echo_n "checking host system type... " >&6; } if ${ac_cv_host+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 $as_echo "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac # Backslashify metacharacters that are still active within # double-quoted strings. sed_quote_subst='s/\(["`$\\]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5 $as_echo_n "checking how to print strings... " >&6; } # Test print first, because it will be a builtin if present. if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='print -r --' elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='printf %s\n' else # Use this function as a fallback that always works. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } ECHO='func_fallback_echo' fi # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "" } case $ECHO in printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5 $as_echo "printf" >&6; } ;; print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5 $as_echo "print -r" >&6; } ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5 $as_echo "cat" >&6; } ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 $as_echo_n "checking for a sed that does not truncate output... " >&6; } if ${ac_cv_path_SED+:} false; then : $as_echo_n "(cached) " >&6 else ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for ac_i in 1 2 3 4 5 6 7; do ac_script="$ac_script$as_nl$ac_script" done echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed { ac_script=; unset ac_script;} if test -z "$SED"; then ac_path_SED_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_SED" || continue # Check for GNU ac_path_SED and select it if it is found. # Check for GNU $ac_path_SED case `"$ac_path_SED" --version 2>&1` in *GNU*) ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo '' >> "conftest.nl" "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_SED_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_SED="$ac_path_SED" ac_path_SED_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_SED_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_SED"; then as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 fi else ac_cv_path_SED=$SED fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 $as_echo "$ac_cv_path_SED" >&6; } SED="$ac_cv_path_SED" rm -f conftest.sed test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 $as_echo_n "checking for fgrep... " >&6; } if ${ac_cv_path_FGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 then ac_cv_path_FGREP="$GREP -F" else if test -z "$FGREP"; then ac_path_FGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in fgrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_FGREP" || continue # Check for GNU ac_path_FGREP and select it if it is found. # Check for GNU $ac_path_FGREP case `"$ac_path_FGREP" --version 2>&1` in *GNU*) ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'FGREP' >> "conftest.nl" "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_FGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_FGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_FGREP"; then as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_FGREP=$FGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 $as_echo "$ac_cv_path_FGREP" >&6; } FGREP="$ac_cv_path_FGREP" test -z "$GREP" && GREP=grep # Check whether --with-gnu-ld was given. if test "${with_gnu_ld+set}" = set; then : withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes else with_gnu_ld=no fi ac_prog=ld if test yes = "$GCC"; then # Check if gcc -print-prog-name=ld gives a path. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 $as_echo_n "checking for ld used by $CC... " >&6; } case $host in *-*-mingw*) # gcc leaves a trailing carriage return, which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [\\/]* | ?:[\\/]*) re_direlt='/[^/][^/]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD=$ac_prog ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test yes = "$with_gnu_ld"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 $as_echo_n "checking for GNU ld... " >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 $as_echo_n "checking for non-GNU ld... " >&6; } fi if ${lt_cv_path_LD+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$LD"; then lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD=$ac_dir/$ac_prog # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &5 $as_echo "$LD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 $as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } if ${lt_cv_prog_gnu_ld+:} false; then : $as_echo_n "(cached) " >&6 else # I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 &5 $as_echo "$lt_cv_prog_gnu_ld" >&6; } with_gnu_ld=$lt_cv_prog_gnu_ld { $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 $as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; } if ${lt_cv_path_NM+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM=$NM else lt_nm_to_check=${ac_tool_prefix}nm if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. tmp_nm=$ac_dir/$lt_tmp_nm if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then # Check to see if the nm accepts a BSD-compat flag. # Adding the 'sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty case $build_os in mingw*) lt_bad_file=conftest.nm/nofile ;; *) lt_bad_file=/dev/null ;; esac case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in *$lt_bad_file* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break 2 ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break 2 ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS=$lt_save_ifs done : ${lt_cv_path_NM=no} fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 $as_echo "$lt_cv_path_NM" >&6; } if test no != "$lt_cv_path_NM"; then NM=$lt_cv_path_NM else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$DUMPBIN"; then : # Let the user override the test. else if test -n "$ac_tool_prefix"; then for ac_prog in dumpbin "link -dump" do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DUMPBIN+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DUMPBIN"; then ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DUMPBIN=$ac_cv_prog_DUMPBIN if test -n "$DUMPBIN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 $as_echo "$DUMPBIN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$DUMPBIN" && break done fi if test -z "$DUMPBIN"; then ac_ct_DUMPBIN=$DUMPBIN for ac_prog in dumpbin "link -dump" do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DUMPBIN"; then ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN if test -n "$ac_ct_DUMPBIN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 $as_echo "$ac_ct_DUMPBIN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_DUMPBIN" && break done if test "x$ac_ct_DUMPBIN" = x; then DUMPBIN=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DUMPBIN=$ac_ct_DUMPBIN fi fi case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols -headers" ;; *) DUMPBIN=: ;; esac fi if test : != "$DUMPBIN"; then NM=$DUMPBIN fi fi test -z "$NM" && NM=nm { $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 $as_echo_n "checking the name lister ($NM) interface... " >&6; } if ${lt_cv_nm_interface+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 (eval echo "\"\$as_me:$LINENO: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 $as_echo "$lt_cv_nm_interface" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 $as_echo_n "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 $as_echo "no, using $LN_S" >&6; } fi # find the maximum length of command line arguments { $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 $as_echo_n "checking the maximum length of command line arguments... " >&6; } if ${lt_cv_sys_max_cmd_len+:} false; then : $as_echo_n "(cached) " >&6 else i=0 teststring=ABCD case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; mint*) # On MiNT this can take a long time and run out of memory. lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; os2*) # The test takes a long time on OS/2. lt_cv_sys_max_cmd_len=8192 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len" && \ test undefined != "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test X`env echo "$teststring$teststring" 2>/dev/null` \ = "X$teststring$teststring"; } >/dev/null 2>&1 && test 17 != "$i" # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac fi if test -n "$lt_cv_sys_max_cmd_len"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 $as_echo "$lt_cv_sys_max_cmd_len" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 $as_echo "none" >&6; } fi max_cmd_len=$lt_cv_sys_max_cmd_len : ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5 $as_echo_n "checking how to convert $build file names to $host format... " >&6; } if ${lt_cv_to_host_file_cmd+:} false; then : $as_echo_n "(cached) " >&6 else case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 ;; esac ;; *-*-cygwin* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_noop ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin ;; esac ;; * ) # unhandled hosts (and "normal" native builds) lt_cv_to_host_file_cmd=func_convert_file_noop ;; esac fi to_host_file_cmd=$lt_cv_to_host_file_cmd { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5 $as_echo "$lt_cv_to_host_file_cmd" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5 $as_echo_n "checking how to convert $build file names to toolchain format... " >&6; } if ${lt_cv_to_tool_file_cmd+:} false; then : $as_echo_n "(cached) " >&6 else #assume ordinary cross tools, or native build. lt_cv_to_tool_file_cmd=func_convert_file_noop case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 ;; esac ;; esac fi to_tool_file_cmd=$lt_cv_to_tool_file_cmd { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5 $as_echo "$lt_cv_to_tool_file_cmd" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 $as_echo_n "checking for $LD option to reload object files... " >&6; } if ${lt_cv_ld_reload_flag+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_reload_flag='-r' fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 $as_echo "$lt_cv_ld_reload_flag" >&6; } reload_flag=$lt_cv_ld_reload_flag case $reload_flag in "" | " "*) ;; *) reload_flag=" $reload_flag" ;; esac reload_cmds='$LD$reload_flag -o $output$reload_objs' case $host_os in cygwin* | mingw* | pw32* | cegcc*) if test yes != "$GCC"; then reload_cmds=false fi ;; darwin*) if test yes = "$GCC"; then reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs' else reload_cmds='$LD$reload_flag -o $output$reload_objs' fi ;; esac if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. set dummy ${ac_tool_prefix}objdump; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OBJDUMP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OBJDUMP"; then ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OBJDUMP=$ac_cv_prog_OBJDUMP if test -n "$OBJDUMP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 $as_echo "$OBJDUMP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OBJDUMP"; then ac_ct_OBJDUMP=$OBJDUMP # Extract the first word of "objdump", so it can be a program name with args. set dummy objdump; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OBJDUMP"; then ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OBJDUMP="objdump" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP if test -n "$ac_ct_OBJDUMP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 $as_echo "$ac_ct_OBJDUMP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OBJDUMP" = x; then OBJDUMP="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OBJDUMP=$ac_ct_OBJDUMP fi else OBJDUMP="$ac_cv_prog_OBJDUMP" fi test -z "$OBJDUMP" && OBJDUMP=objdump { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 $as_echo_n "checking how to recognize dependent libraries... " >&6; } if ${lt_cv_deplibs_check_method+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_file_magic_cmd='$MAGIC_CMD' lt_cv_file_magic_test_file= lt_cv_deplibs_check_method='unknown' # Need to set the preceding variable on all platforms that support # interlibrary dependencies. # 'none' -- dependencies not supported. # 'unknown' -- same as none, but documents that we really don't know. # 'pass_all' -- all dependencies passed with no checks. # 'test_compile' -- check by making test program. # 'file_magic [[regex]]' -- check by looking for files in library path # that responds to the $file_magic_cmd with a given extended regex. # If you have 'file' or equivalent on your system and you're not sure # whether 'pass_all' will *always* work, you probably want this one. case $host_os in aix[4-9]*) lt_cv_deplibs_check_method=pass_all ;; beos*) lt_cv_deplibs_check_method=pass_all ;; bsdi[45]*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' lt_cv_file_magic_cmd='/usr/bin/file -L' lt_cv_file_magic_test_file=/shlib/libc.so ;; cygwin*) # func_win32_libid is a shell function defined in ltmain.sh lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' ;; mingw* | pw32*) # Base MSYS/MinGW do not provide the 'file' command needed by # func_win32_libid shell function, so use a weaker test based on 'objdump', # unless we find 'file', for example because we are cross-compiling. if ( file / ) >/dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else # Keep this pattern in sync with the one in func_win32_libid. lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc*) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; haiku*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]' lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[3-9]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd* | bitrig*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; os2*) lt_cv_deplibs_check_method=pass_all ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 $as_echo "$lt_cv_deplibs_check_method" >&6; } file_magic_glob= want_nocaseglob=no if test "$build" = "$host"; then case $host_os in mingw* | pw32*) if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then want_nocaseglob=yes else file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"` fi ;; esac fi file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. set dummy ${ac_tool_prefix}dlltool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DLLTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DLLTOOL"; then ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DLLTOOL=$ac_cv_prog_DLLTOOL if test -n "$DLLTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 $as_echo "$DLLTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_DLLTOOL"; then ac_ct_DLLTOOL=$DLLTOOL # Extract the first word of "dlltool", so it can be a program name with args. set dummy dlltool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DLLTOOL"; then ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DLLTOOL="dlltool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL if test -n "$ac_ct_DLLTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 $as_echo "$ac_ct_DLLTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_DLLTOOL" = x; then DLLTOOL="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DLLTOOL=$ac_ct_DLLTOOL fi else DLLTOOL="$ac_cv_prog_DLLTOOL" fi test -z "$DLLTOOL" && DLLTOOL=dlltool { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5 $as_echo_n "checking how to associate runtime and link libraries... " >&6; } if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_sharedlib_from_linklib_cmd='unknown' case $host_os in cygwin* | mingw* | pw32* | cegcc*) # two different shell functions defined in ltmain.sh; # decide which one to use based on capabilities of $DLLTOOL case `$DLLTOOL --help 2>&1` in *--identify-strict*) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib ;; *) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback ;; esac ;; *) # fallback: assume linklib IS sharedlib lt_cv_sharedlib_from_linklib_cmd=$ECHO ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5 $as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; } sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO if test -n "$ac_tool_prefix"; then for ac_prog in ar do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AR="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AR=$ac_cv_prog_AR if test -n "$AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 $as_echo "$AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AR" && break done fi if test -z "$AR"; then ac_ct_AR=$AR for ac_prog in ar do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_AR="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 $as_echo "$ac_ct_AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_AR" && break done if test "x$ac_ct_AR" = x; then AR="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR fi fi : ${AR=ar} # Use ARFLAGS variable as AR's operation code to sync the variable naming with # Automake. If both AR_FLAGS and ARFLAGS are specified, AR_FLAGS should have # higher priority because thats what people were doing historically (setting # ARFLAGS for automake and AR_FLAGS for libtool). FIXME: Make the AR_FLAGS # variable obsoleted/removed. test ${AR_FLAGS+y} || AR_FLAGS=${ARFLAGS-cr} lt_ar_flags=$AR_FLAGS # Make AR_FLAGS overridable by 'make ARFLAGS='. Don't try to run-time override # by AR_FLAGS because that was never working and AR_FLAGS is about to die. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5 $as_echo_n "checking for archiver @FILE support... " >&6; } if ${lt_cv_ar_at_file+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ar_at_file=no cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : echo conftest.$ac_objext > conftest.lst lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5' { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 (eval $lt_ar_try) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test 0 -eq "$ac_status"; then # Ensure the archiver fails upon bogus file names. rm -f conftest.$ac_objext libconftest.a { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 (eval $lt_ar_try) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test 0 -ne "$ac_status"; then lt_cv_ar_at_file=@ fi fi rm -f conftest.* libconftest.a fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5 $as_echo "$lt_cv_ar_at_file" >&6; } if test no = "$lt_cv_ar_at_file"; then archiver_list_spec= else archiver_list_spec=$lt_cv_ar_at_file fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi test -z "$STRIP" && STRIP=: if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 $as_echo "$RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 $as_echo "$ac_ct_RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi test -z "$RANLIB" && RANLIB=: # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in bitrig* | openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" fi case $host_os in darwin*) lock_old_archive_extraction=yes ;; *) lock_old_archive_extraction=no ;; esac # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Check for command to grab the raw symbol name followed by C symbol from nm. { $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 $as_echo_n "checking command to parse $NM output from $compiler object... " >&6; } if ${lt_cv_sys_global_symbol_pipe+:} false; then : $as_echo_n "(cached) " >&6 else # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[BCDEGRST]' # Regexp to match symbols that can be accessed directly from C. sympat='\([_A-Za-z][_A-Za-z0-9]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[BCDT]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[ABCDGISTW]' ;; hpux*) if test ia64 = "$host_cpu"; then symcode='[ABCDEGRST]' fi ;; irix* | nonstopux*) symcode='[BCDEGRST]' ;; osf*) symcode='[BCDEGQRST]' ;; solaris*) symcode='[BDRT]' ;; sco3.2v5*) symcode='[DT]' ;; sysv4.2uw2*) symcode='[DT]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[ABDT]' ;; sysv4) symcode='[DFNSTU]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[ABCDGIRSTW]' ;; esac if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Gets list of data symbols to import. lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" # Adjust the below global symbol transforms to fixup imported variables. lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" lt_c_name_lib_hook="\ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" else # Disable hooks by default. lt_cv_sys_global_symbol_to_import= lt_cdecl_hook= lt_c_name_hook= lt_c_name_lib_hook= fi # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="sed -n"\ $lt_cdecl_hook\ " -e 's/^T .* \(.*\)$/extern int \1();/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ $lt_c_name_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" # Transform an extracted symbol line into symbol name with lib prefix and # symbol address. lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ $lt_c_name_lib_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function, # D for any global variable and I for any imported variable. # Also find C++ and __fastcall symbols from MSVC++ or ICC, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK '"\ " {last_section=section; section=\$ 3};"\ " /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ " /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ " /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ " {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ " s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx" else lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then # Now try to grab the symbols. nlist=conftest.nm if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5 (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ LT_DLSYM_CONST struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_globsym_save_LIBS=$LIBS lt_globsym_save_CFLAGS=$CFLAGS LIBS=conftstm.$ac_objext CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s conftest$ac_exeext; then pipe_works=yes fi LIBS=$lt_globsym_save_LIBS CFLAGS=$lt_globsym_save_CFLAGS else echo "cannot find nm_test_func in $nlist" >&5 fi else echo "cannot find nm_test_var in $nlist" >&5 fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 fi else echo "$progname: failed program was:" >&5 cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test yes = "$pipe_works"; then break else lt_cv_sys_global_symbol_pipe= fi done fi if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 $as_echo "failed" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 $as_echo "ok" >&6; } fi # Response file support. if test "$lt_cv_nm_interface" = "MS dumpbin"; then nm_file_list_spec='@' elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then nm_file_list_spec='@' fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5 $as_echo_n "checking for sysroot... " >&6; } # Check whether --with-sysroot was given. if test "${with_sysroot+set}" = set; then : withval=$with_sysroot; else with_sysroot=no fi lt_sysroot= case $with_sysroot in #( yes) if test yes = "$GCC"; then lt_sysroot=`$CC --print-sysroot 2>/dev/null` fi ;; #( /*) lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` ;; #( no|'') ;; #( *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_sysroot" >&5 $as_echo "$with_sysroot" >&6; } as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5 $as_echo "${lt_sysroot:-no}" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a working dd" >&5 $as_echo_n "checking for a working dd... " >&6; } if ${ac_cv_path_lt_DD+:} false; then : $as_echo_n "(cached) " >&6 else printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i : ${lt_DD:=$DD} if test -z "$lt_DD"; then ac_path_lt_DD_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in dd; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_lt_DD="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_lt_DD" || continue if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: fi $ac_path_lt_DD_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_lt_DD"; then : fi else ac_cv_path_lt_DD=$lt_DD fi rm -f conftest.i conftest2.i conftest.out fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_lt_DD" >&5 $as_echo "$ac_cv_path_lt_DD" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to truncate binary pipes" >&5 $as_echo_n "checking how to truncate binary pipes... " >&6; } if ${lt_cv_truncate_bin+:} false; then : $as_echo_n "(cached) " >&6 else printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i lt_cv_truncate_bin= if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" fi rm -f conftest.i conftest2.i conftest.out test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_truncate_bin" >&5 $as_echo "$lt_cv_truncate_bin" >&6; } # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in $*""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } # Check whether --enable-libtool-lock was given. if test "${enable_libtool_lock+set}" = set; then : enableval=$enable_libtool_lock; fi test no = "$enable_libtool_lock" || enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out what ABI is being produced by ac_compile, and set mode # options accordingly. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE=32 ;; *ELF-64*) HPUX_IA64_MODE=64 ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '#line '$LINENO' "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then if test yes = "$lt_cv_prog_gnu_ld"; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; mips64*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '#line '$LINENO' "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then emul=elf case `/usr/bin/file conftest.$ac_objext` in *32-bit*) emul="${emul}32" ;; *64-bit*) emul="${emul}64" ;; esac case `/usr/bin/file conftest.$ac_objext` in *MSB*) emul="${emul}btsmip" ;; *LSB*) emul="${emul}ltsmip" ;; esac case `/usr/bin/file conftest.$ac_objext` in *N32*) emul="${emul}n32" ;; esac LD="${LD-ld} -m $emul" fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. Note that the listed cases only cover the # situations where additional linker options are needed (such as when # doing 32-bit compilation for a host where ld defaults to 64-bit, or # vice versa); the common cases where no linker options are needed do # not appear in the list. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) case `/usr/bin/file conftest.o` in *x86-64*) LD="${LD-ld} -m elf32_x86_64" ;; *) LD="${LD-ld} -m elf_i386" ;; esac ;; powerpc64le-*linux*) LD="${LD-ld} -m elf32lppclinux" ;; powerpc64-*linux*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; powerpcle-*linux*) LD="${LD-ld} -m elf64lppc" ;; powerpc-*linux*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -belf" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 $as_echo_n "checking whether the C compiler needs -belf... " >&6; } if ${lt_cv_cc_needs_belf+:} false; then : $as_echo_n "(cached) " >&6 else ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_cc_needs_belf=yes else lt_cv_cc_needs_belf=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 $as_echo "$lt_cv_cc_needs_belf" >&6; } if test yes != "$lt_cv_cc_needs_belf"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS=$SAVE_CFLAGS fi ;; *-*solaris*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) case $host in i?86-*-solaris*|x86_64-*-solaris*) LD="${LD-ld} -m elf_x86_64" ;; sparc*-*-solaris*) LD="${LD-ld} -m elf64_sparc" ;; esac # GNU ld 2.21 introduced _sol2 emulations. Use them if available. if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then LD=${LD-ld}_sol2 fi ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks=$enable_libtool_lock if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args. set dummy ${ac_tool_prefix}mt; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_MANIFEST_TOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$MANIFEST_TOOL"; then ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL if test -n "$MANIFEST_TOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5 $as_echo "$MANIFEST_TOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_MANIFEST_TOOL"; then ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL # Extract the first word of "mt", so it can be a program name with args. set dummy mt; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_MANIFEST_TOOL"; then ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_MANIFEST_TOOL="mt" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL if test -n "$ac_ct_MANIFEST_TOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5 $as_echo "$ac_ct_MANIFEST_TOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_MANIFEST_TOOL" = x; then MANIFEST_TOOL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL fi else MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL" fi test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5 $as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; } if ${lt_cv_path_mainfest_tool+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_path_mainfest_tool=no echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5 $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out cat conftest.err >&5 if $GREP 'Manifest Tool' conftest.out > /dev/null; then lt_cv_path_mainfest_tool=yes fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5 $as_echo "$lt_cv_path_mainfest_tool" >&6; } if test yes != "$lt_cv_path_mainfest_tool"; then MANIFEST_TOOL=: fi case $host_os in rhapsody* | darwin*) if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DSYMUTIL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DSYMUTIL"; then ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DSYMUTIL=$ac_cv_prog_DSYMUTIL if test -n "$DSYMUTIL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 $as_echo "$DSYMUTIL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_DSYMUTIL"; then ac_ct_DSYMUTIL=$DSYMUTIL # Extract the first word of "dsymutil", so it can be a program name with args. set dummy dsymutil; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DSYMUTIL"; then ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL if test -n "$ac_ct_DSYMUTIL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 $as_echo "$ac_ct_DSYMUTIL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_DSYMUTIL" = x; then DSYMUTIL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DSYMUTIL=$ac_ct_DSYMUTIL fi else DSYMUTIL="$ac_cv_prog_DSYMUTIL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. set dummy ${ac_tool_prefix}nmedit; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_NMEDIT+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$NMEDIT"; then ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi NMEDIT=$ac_cv_prog_NMEDIT if test -n "$NMEDIT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 $as_echo "$NMEDIT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_NMEDIT"; then ac_ct_NMEDIT=$NMEDIT # Extract the first word of "nmedit", so it can be a program name with args. set dummy nmedit; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_NMEDIT"; then ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_NMEDIT="nmedit" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT if test -n "$ac_ct_NMEDIT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 $as_echo "$ac_ct_NMEDIT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_NMEDIT" = x; then NMEDIT=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac NMEDIT=$ac_ct_NMEDIT fi else NMEDIT="$ac_cv_prog_NMEDIT" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. set dummy ${ac_tool_prefix}lipo; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_LIPO+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$LIPO"; then ac_cv_prog_LIPO="$LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_LIPO="${ac_tool_prefix}lipo" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi LIPO=$ac_cv_prog_LIPO if test -n "$LIPO"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 $as_echo "$LIPO" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_LIPO"; then ac_ct_LIPO=$LIPO # Extract the first word of "lipo", so it can be a program name with args. set dummy lipo; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_LIPO+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_LIPO"; then ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_LIPO="lipo" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO if test -n "$ac_ct_LIPO"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 $as_echo "$ac_ct_LIPO" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_LIPO" = x; then LIPO=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac LIPO=$ac_ct_LIPO fi else LIPO="$ac_cv_prog_LIPO" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. set dummy ${ac_tool_prefix}otool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OTOOL"; then ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OTOOL="${ac_tool_prefix}otool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OTOOL=$ac_cv_prog_OTOOL if test -n "$OTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 $as_echo "$OTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL"; then ac_ct_OTOOL=$OTOOL # Extract the first word of "otool", so it can be a program name with args. set dummy otool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OTOOL"; then ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OTOOL="otool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL if test -n "$ac_ct_OTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 $as_echo "$ac_ct_OTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OTOOL" = x; then OTOOL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL=$ac_ct_OTOOL fi else OTOOL="$ac_cv_prog_OTOOL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. set dummy ${ac_tool_prefix}otool64; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OTOOL64+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OTOOL64"; then ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OTOOL64=$ac_cv_prog_OTOOL64 if test -n "$OTOOL64"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 $as_echo "$OTOOL64" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL64"; then ac_ct_OTOOL64=$OTOOL64 # Extract the first word of "otool64", so it can be a program name with args. set dummy otool64; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OTOOL64"; then ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OTOOL64="otool64" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 if test -n "$ac_ct_OTOOL64"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 $as_echo "$ac_ct_OTOOL64" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OTOOL64" = x; then OTOOL64=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL64=$ac_ct_OTOOL64 fi else OTOOL64="$ac_cv_prog_OTOOL64" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 $as_echo_n "checking for -single_module linker flag... " >&6; } if ${lt_cv_apple_cc_single_mod+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_apple_cc_single_mod=no if test -z "$LT_MULTI_MODULE"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&5 $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? # If there is a non-empty error log, and "single_module" # appears in it, assume the flag caused a linker warning if test -s conftest.err && $GREP single_module conftest.err; then cat conftest.err >&5 # Otherwise, if the output was created with a 0 exit code from # the compiler, it worked. elif test -f libconftest.dylib && test 0 = "$_lt_result"; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&5 fi rm -rf libconftest.dylib* rm -f conftest.* fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 $as_echo "$lt_cv_apple_cc_single_mod" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 $as_echo_n "checking for -exported_symbols_list linker flag... " >&6; } if ${lt_cv_ld_exported_symbols_list+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_ld_exported_symbols_list=yes else lt_cv_ld_exported_symbols_list=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 $as_echo "$lt_cv_ld_exported_symbols_list" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5 $as_echo_n "checking for -force_load linker flag... " >&6; } if ${lt_cv_ld_force_load+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_force_load=no cat > conftest.c << _LT_EOF int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5 $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 echo "$AR $AR_FLAGS libconftest.a conftest.o" >&5 $AR $AR_FLAGS libconftest.a conftest.o 2>&5 echo "$RANLIB libconftest.a" >&5 $RANLIB libconftest.a 2>&5 cat > conftest.c << _LT_EOF int main() { return 0;} _LT_EOF echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5 $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err _lt_result=$? if test -s conftest.err && $GREP force_load conftest.err; then cat conftest.err >&5 elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then lt_cv_ld_force_load=yes else cat conftest.err >&5 fi rm -f conftest.err libconftest.a conftest conftest.c rm -rf conftest.dSYM fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 $as_echo "$lt_cv_ld_force_load" >&6; } case $host_os in rhapsody* | darwin1.[012]) _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; darwin*) # darwin 5.x on # if running on 10.5 or later, the deployment target defaults # to the OS version, if on x86, and 10.4, the deployment # target defaults to 10.4. Don't you love it? case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in 10.0,*86*-darwin8*|10.0,*-darwin[91]*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; 10.[012][,.]*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; 10.*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test yes = "$lt_cv_apple_cc_single_mod"; then _lt_dar_single_mod='$single_module' fi if test yes = "$lt_cv_ld_exported_symbols_list"; then _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' fi if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac # func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x$2 in x) ;; *:) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" ;; x:*) eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" ;; *::*) eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" ;; *) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" ;; esac } for ac_header in dlfcn.h do : ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default " if test "x$ac_cv_header_dlfcn_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_DLFCN_H 1 _ACEOF fi done # Set options enable_dlopen=no enable_win32_dll=no # Check whether --enable-shared was given. if test "${enable_shared+set}" = set; then : enableval=$enable_shared; p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS=$lt_save_ifs ;; esac else enable_shared=yes fi # Check whether --enable-static was given. if test "${enable_static+set}" = set; then : enableval=$enable_static; p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS=$lt_save_ifs ;; esac else enable_static=yes fi # Check whether --with-pic was given. if test "${with_pic+set}" = set; then : withval=$with_pic; lt_p=${PACKAGE-default} case $withval in yes|no) pic_mode=$withval ;; *) pic_mode=default # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for lt_pkg in $withval; do IFS=$lt_save_ifs if test "X$lt_pkg" = "X$lt_p"; then pic_mode=yes fi done IFS=$lt_save_ifs ;; esac else pic_mode=default fi # Check whether --enable-fast-install was given. if test "${enable_fast_install+set}" = set; then : enableval=$enable_fast_install; p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS=$lt_save_ifs ;; esac else enable_fast_install=yes fi shared_archive_member_spec= case $host,$enable_shared in power*-*-aix[5-9]*,yes) { $as_echo "$as_me:${as_lineno-$LINENO}: checking which variant of shared library versioning to provide" >&5 $as_echo_n "checking which variant of shared library versioning to provide... " >&6; } # Check whether --with-aix-soname was given. if test "${with_aix_soname+set}" = set; then : withval=$with_aix_soname; case $withval in aix|svr4|both) ;; *) as_fn_error $? "Unknown argument to --with-aix-soname" "$LINENO" 5 ;; esac lt_cv_with_aix_soname=$with_aix_soname else if ${lt_cv_with_aix_soname+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_with_aix_soname=aix fi with_aix_soname=$lt_cv_with_aix_soname fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_aix_soname" >&5 $as_echo "$with_aix_soname" >&6; } if test aix != "$with_aix_soname"; then # For the AIX way of multilib, we name the shared archive member # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, # the AIX toolchain works better with OBJECT_MODE set (default 32). if test 64 = "${OBJECT_MODE-32}"; then shared_archive_member_spec=shr_64 else shared_archive_member_spec=shr fi fi ;; *) with_aix_soname=aix ;; esac # This can be used to rebuild libtool when needed LIBTOOL_DEPS=$ltmain # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' test -z "$LN_S" && LN_S="ln -s" if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 $as_echo_n "checking for objdir... " >&6; } if ${lt_cv_objdir+:} false; then : $as_echo_n "(cached) " >&6 else rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 $as_echo "$lt_cv_objdir" >&6; } objdir=$lt_cv_objdir cat >>confdefs.h <<_ACEOF #define LT_OBJDIR "$lt_cv_objdir/" _ACEOF case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a '.a' archive for static linking (except MSVC and # ICC, which need '.lib'). libext=a with_gnu_ld=$lt_cv_prog_gnu_ld old_CC=$CC old_CFLAGS=$CFLAGS # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o func_cc_basename $compiler cc_basename=$func_cc_basename_result # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 $as_echo_n "checking for ${ac_tool_prefix}file... " >&6; } if ${lt_cv_path_MAGIC_CMD+:} false; then : $as_echo_n "(cached) " >&6 else case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/${ac_tool_prefix}file"; then lt_cv_path_MAGIC_CMD=$ac_dir/"${ac_tool_prefix}file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac fi MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 $as_echo "$MAGIC_CMD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5 $as_echo_n "checking for file... " >&6; } if ${lt_cv_path_MAGIC_CMD+:} false; then : $as_echo_n "(cached) " >&6 else case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/file"; then lt_cv_path_MAGIC_CMD=$ac_dir/"file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac fi MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 $as_echo "$MAGIC_CMD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi else MAGIC_CMD=: fi fi fi ;; esac # Use C for the default configuration in the libtool script lt_save_CC=$CC ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o objext=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then lt_prog_compiler_no_builtin_flag= if test yes = "$GCC"; then case $cc_basename in nvcc*) lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;; *) lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 $as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_rtti_exceptions=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-fno-rtti -fno-exceptions" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_rtti_exceptions=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 $as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; } if test yes = "$lt_cv_prog_compiler_rtti_exceptions"; then lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" else : fi fi lt_prog_compiler_wl= lt_prog_compiler_pic= lt_prog_compiler_static= if test yes = "$GCC"; then lt_prog_compiler_wl='-Wl,' lt_prog_compiler_static='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' fi lt_prog_compiler_pic='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support lt_prog_compiler_pic='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries lt_prog_compiler_pic='-DDLL_EXPORT' case $host_os in os2*) lt_prog_compiler_static='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic='-fno-common' ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. lt_prog_compiler_static= ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) lt_prog_compiler_pic='-fPIC' ;; esac ;; interix[3-9]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. lt_prog_compiler_can_build_shared=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic=-Kconform_pic fi ;; *) lt_prog_compiler_pic='-fPIC' ;; esac case $cc_basename in nvcc*) # Cuda Compiler Driver 2.2 lt_prog_compiler_wl='-Xlinker ' if test -n "$lt_prog_compiler_pic"; then lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic" fi ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) lt_prog_compiler_wl='-Wl,' if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' else lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' fi ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic='-fno-common' case $cc_basename in nagfor*) # NAG Fortran compiler lt_prog_compiler_wl='-Wl,-Wl,,' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; esac ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). lt_prog_compiler_pic='-DDLL_EXPORT' case $host_os in os2*) lt_prog_compiler_static='$wl-static' ;; esac ;; hpux9* | hpux10* | hpux11*) lt_prog_compiler_wl='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) lt_prog_compiler_pic='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? lt_prog_compiler_static='$wl-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) lt_prog_compiler_wl='-Wl,' # PIC (with -KPIC) is the default. lt_prog_compiler_static='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in # old Intel for x86_64, which still supported -KPIC. ecc*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; # Lahey Fortran 8.1. lf95*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='--shared' lt_prog_compiler_static='--static' ;; nagfor*) # NAG Fortran compiler lt_prog_compiler_wl='-Wl,-Wl,,' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; tcc*) # Fabrice Bellard et al's Tiny C Compiler lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fpic' lt_prog_compiler_static='-Bstatic' ;; ccc*) lt_prog_compiler_wl='-Wl,' # All Alpha code is PIC. lt_prog_compiler_static='-non_shared' ;; xl* | bgxl* | bgf* | mpixl*) # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-qpic' lt_prog_compiler_static='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='' ;; *Sun\ F* | *Sun*Fortran*) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='-Qoption ld ' ;; *Sun\ C*) # Sun C 5.9 lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='-Wl,' ;; *Intel*\ [CF]*Compiler*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; *Portland\ Group*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fpic' lt_prog_compiler_static='-Bstatic' ;; esac ;; esac ;; newsos6) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; osf3* | osf4* | osf5*) lt_prog_compiler_wl='-Wl,' # All OSF/1 code is PIC. lt_prog_compiler_static='-non_shared' ;; rdos*) lt_prog_compiler_static='-non_shared' ;; solaris*) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) lt_prog_compiler_wl='-Qoption ld ';; *) lt_prog_compiler_wl='-Wl,';; esac ;; sunos4*) lt_prog_compiler_wl='-Qoption ld ' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic='-Kconform_pic' lt_prog_compiler_static='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; unicos*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_can_build_shared=no ;; uts4*) lt_prog_compiler_pic='-pic' lt_prog_compiler_static='-Bstatic' ;; *) lt_prog_compiler_can_build_shared=no ;; esac fi case $host_os in # For platforms that do not support PIC, -DPIC is meaningless: *djgpp*) lt_prog_compiler_pic= ;; *) lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 $as_echo_n "checking for $compiler option to produce PIC... " >&6; } if ${lt_cv_prog_compiler_pic+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic=$lt_prog_compiler_pic fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5 $as_echo "$lt_cv_prog_compiler_pic" >&6; } lt_prog_compiler_pic=$lt_cv_prog_compiler_pic # # Check to make sure the PIC flag actually works. # if test -n "$lt_prog_compiler_pic"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 $as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } if ${lt_cv_prog_compiler_pic_works+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic_works=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$lt_prog_compiler_pic -DPIC" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_pic_works=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 $as_echo "$lt_cv_prog_compiler_pic_works" >&6; } if test yes = "$lt_cv_prog_compiler_pic_works"; then case $lt_prog_compiler_pic in "" | " "*) ;; *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; esac else lt_prog_compiler_pic= lt_prog_compiler_can_build_shared=no fi fi # # Check to make sure the static flag actually works. # wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 $as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } if ${lt_cv_prog_compiler_static_works+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_static_works=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $lt_tmp_static_flag" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_static_works=yes fi else lt_cv_prog_compiler_static_works=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 $as_echo "$lt_cv_prog_compiler_static_works" >&6; } if test yes = "$lt_cv_prog_compiler_static_works"; then : else lt_prog_compiler_static= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 $as_echo "$lt_cv_prog_compiler_c_o" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 $as_echo "$lt_cv_prog_compiler_c_o" >&6; } hard_links=nottested if test no = "$lt_cv_prog_compiler_c_o" && test no != "$need_locks"; then # do not overwrite the value of need_locks provided by the user { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 $as_echo_n "checking if we can lock with hard links... " >&6; } hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 $as_echo "$hard_links" >&6; } if test no = "$hard_links"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5 $as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;} need_locks=warn fi else need_locks=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 $as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } runpath_var= allow_undefined_flag= always_export_symbols=no archive_cmds= archive_expsym_cmds= compiler_needs_object=no enable_shared_with_static_runtimes=no export_dynamic_flag_spec= export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' hardcode_automatic=no hardcode_direct=no hardcode_direct_absolute=no hardcode_libdir_flag_spec= hardcode_libdir_separator= hardcode_minus_L=no hardcode_shlibpath_var=unsupported inherit_rpath=no link_all_deplibs=unknown module_cmds= module_expsym_cmds= old_archive_from_new_cmds= old_archive_from_expsyms_cmds= thread_safe_flag_spec= whole_archive_flag_spec= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list include_expsyms= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ' (' and ')$', so one must not match beginning or # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', # as well as any symbol that contains 'd'. exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ and ICC port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++ or Intel C++ Compiler. if test yes != "$GCC"; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++ or ICC) with_gnu_ld=yes ;; openbsd* | bitrig*) with_gnu_ld=no ;; esac ld_shlibs=yes # On some targets, GNU ld is compatible enough with the native linker # that we're better off using the native interface for both. lt_use_gnu_ld_interface=no if test yes = "$with_gnu_ld"; then case $host_os in aix*) # The AIX port of GNU ld has always aspired to compatibility # with the native linker. However, as the warning in the GNU ld # block says, versions before 2.19.5* couldn't really create working # shared libraries, regardless of the interface used. case `$LD -v 2>&1` in *\ \(GNU\ Binutils\)\ 2.19.5*) ;; *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;; *\ \(GNU\ Binutils\)\ [3-9]*) ;; *) lt_use_gnu_ld_interface=yes ;; esac ;; *) lt_use_gnu_ld_interface=yes ;; esac fi if test yes = "$lt_use_gnu_ld_interface"; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='$wl' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' export_dynamic_flag_spec='$wl--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then whole_archive_flag_spec=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else whole_archive_flag_spec= fi supports_anon_versioning=no case `$LD -v | $SED -e 's/(^)\+)\s\+//' 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[3-9]*) # On AIX/PPC, the GNU linker is very broken if test ia64 != "$host_cpu"; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.19, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to install binutils *** 2.20 or above, or modify your PATH so that a non-GNU linker is found. *** You will then need to restart the configuration process. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then allow_undefined_flag=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else ld_shlibs=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, # as there is no search path for DLLs. hardcode_libdir_flag_spec='-L$libdir' export_dynamic_flag_spec='$wl--export-all-symbols' allow_undefined_flag=unsupported always_export_symbols=no enable_shared_with_static_runtimes=yes export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else ld_shlibs=no fi ;; haiku*) archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' link_all_deplibs=yes ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes allow_undefined_flag=unsupported shrext_cmds=.dll archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes=yes file_list_spec='@' ;; interix[3-9]*) hardcode_direct=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='$wl-rpath,$libdir' export_dynamic_flag_spec='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' archive_expsym_cmds='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test linux-dietlibc = "$host_os"; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test no = "$tmp_diet" then tmp_addflag=' $pic_flag' tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group f77 and f90 compilers whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 whole_archive_flag_spec= tmp_sharedflag='--shared' ;; nagfor*) # NAGFOR 5.3 tmp_sharedflag='-Wl,-shared' ;; xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; nvcc*) # Cuda Compiler Driver 2.2 whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' compiler_needs_object=yes ;; esac case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 whole_archive_flag_spec='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' compiler_needs_object=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi case $cc_basename in tcc*) export_dynamic_flag_spec='-rdynamic' ;; xlf* | bgf* | bgxlf* | mpixlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test yes = "$supports_anon_versioning"; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else ld_shlibs=no fi ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac ;; sunos4*) archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= hardcode_direct=yes hardcode_shlibpath_var=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac if test no = "$ld_shlibs"; then runpath_var= hardcode_libdir_flag_spec= export_dynamic_flag_spec= whole_archive_flag_spec= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) allow_undefined_flag=unsupported always_export_symbols=yes archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. hardcode_minus_L=yes if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. hardcode_direct=unsupported fi ;; aix[4-9]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else export_symbols_cmds='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "L") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then aix_use_runtimelinking=yes break fi done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. archive_cmds='' hardcode_direct=yes hardcode_direct_absolute=yes hardcode_libdir_separator=':' link_all_deplibs=yes file_list_spec='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # traditional, no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. hardcode_direct=no hardcode_direct_absolute=no ;; esac if test yes = "$GCC"; then case $host_os in aix4.[012]|aix4.[012].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 hardcode_direct=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking hardcode_minus_L=yes hardcode_libdir_flag_spec='-L$libdir' hardcode_libdir_separator= fi ;; esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag="$shared_flag "'$wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi export_dynamic_flag_spec='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. always_export_symbols=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. allow_undefined_flag='-berok' # Determine the default libpath from the value encoded in an # empty executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath_+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath_ fi hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then hardcode_libdir_flag_spec='$wl-R $libdir:/usr/lib:/lib' allow_undefined_flag="-z nodefs" archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath_+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath_ fi hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. no_undefined_flag=' $wl-bernotok' allow_undefined_flag=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. whole_archive_flag_spec='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives whole_archive_flag_spec='$convenience' fi archive_cmds_need_lc=yes archive_expsym_cmds='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared libraries. archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols archive_expsym_cmds="$archive_expsym_cmds"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi archive_expsym_cmds="$archive_expsym_cmds"'~$RM -r $output_objdir/$realname.d' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; bsdi[45]*) export_dynamic_flag_spec=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++ or Intel C++ Compiler. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in cl* | icl*) # Native MSVC or ICC hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported always_export_symbols=yes file_list_spec='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, )='true' enable_shared_with_static_runtimes=yes exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' # Don't use ranlib old_postinstall_cmds='chmod 644 $oldlib' postlink_cmds='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # Assume MSVC and ICC wrapper hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. old_archive_from_new_cmds='true' # FIXME: Should let the user specify the lib program. old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' enable_shared_with_static_runtimes=yes ;; esac ;; darwin* | rhapsody*) archive_cmds_need_lc=no hardcode_direct=no hardcode_automatic=yes hardcode_shlibpath_var=unsupported if test yes = "$lt_cv_ld_force_load"; then whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' else whole_archive_flag_spec='' fi link_all_deplibs=yes allow_undefined_flag=$_lt_dar_allow_undefined case $cc_basename in ifort*|nagfor*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test yes = "$_lt_dar_can_shared"; then output_verbose_link_cmd=func_echo_all archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" archive_expsym_cmds="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" module_expsym_cmds="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" else ld_shlibs=no fi ;; dgux*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2.*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly*) archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; hpux9*) if test yes = "$GCC"; then archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' fi hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes export_dynamic_flag_spec='$wl-E' ;; hpux10*) if test yes,no = "$GCC,$with_gnu_ld"; then archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test no = "$with_gnu_ld"; then hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes fi ;; hpux11*) if test yes,no = "$GCC,$with_gnu_ld"; then case $host_cpu in hppa*64*) archive_cmds='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) archive_cmds='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) # Older versions of the 11.00 compiler do not understand -b yet # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5 $as_echo_n "checking if $CC understands -b... " >&6; } if ${lt_cv_prog_compiler__b+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler__b=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -b" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler__b=yes fi else lt_cv_prog_compiler__b=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5 $as_echo "$lt_cv_prog_compiler__b" >&6; } if test yes = "$lt_cv_prog_compiler__b"; then archive_cmds='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi ;; esac fi if test no = "$with_gnu_ld"; then hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: case $host_cpu in hppa*64*|ia64*) hardcode_direct=no hardcode_shlibpath_var=no ;; *) hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test yes = "$GCC"; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. # This should be the same for all languages, so no per-tag cache variable. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5 $as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; } if ${lt_cv_irix_exported_symbol+:} false; then : $as_echo_n "(cached) " >&6 else save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int foo (void) { return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_irix_exported_symbol=yes else lt_cv_irix_exported_symbol=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5 $as_echo "$lt_cv_irix_exported_symbol" >&6; } if test yes = "$lt_cv_irix_exported_symbol"; then archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' fi else archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: inherit_rpath=yes link_all_deplibs=yes ;; linux*) case $cc_basename in tcc*) # Fabrice Bellard et al's Tiny C Compiler ld_shlibs=yes archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; newsos6) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: hardcode_shlibpath_var=no ;; *nto* | *qnx*) ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then hardcode_direct=yes hardcode_shlibpath_var=no hardcode_direct_absolute=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' hardcode_libdir_flag_spec='$wl-rpath,$libdir' export_dynamic_flag_spec='$wl-E' else archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='$wl-rpath,$libdir' fi else ld_shlibs=no fi ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes allow_undefined_flag=unsupported shrext_cmds=.dll archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes=yes file_list_spec='@' ;; osf3*) if test yes = "$GCC"; then allow_undefined_flag=' $wl-expect_unresolved $wl\*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test yes = "$GCC"; then allow_undefined_flag=' $wl-expect_unresolved $wl\*' archive_cmds='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly hardcode_libdir_flag_spec='-rpath $libdir' fi archive_cmds_need_lc='no' hardcode_libdir_separator=: ;; solaris*) no_undefined_flag=' -z defs' if test yes = "$GCC"; then wlarc='$wl' archive_cmds='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' archive_cmds='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='$wl' archive_cmds='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi hardcode_libdir_flag_spec='-R$libdir' hardcode_shlibpath_var=no case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. GCC discards it without '$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test yes = "$GCC"; then whole_archive_flag_spec='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' else whole_archive_flag_spec='-z allextract$convenience -z defaultextract' fi ;; esac link_all_deplibs=yes ;; sunos4*) if test sequent = "$host_vendor"; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. archive_cmds='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi hardcode_libdir_flag_spec='-L$libdir' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; sysv4) case $host_vendor in sni) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' reload_cmds='$CC -r -o $output$reload_objs' hardcode_direct=no ;; motorola) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' hardcode_shlibpath_var=no ;; sysv4.3*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no export_dynamic_flag_spec='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes ld_shlibs=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) no_undefined_flag='$wl-z,text' archive_cmds_need_lc=no hardcode_shlibpath_var=no runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. no_undefined_flag='$wl-z,text' allow_undefined_flag='$wl-z,nodefs' archive_cmds_need_lc=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='$wl-R,$libdir' hardcode_libdir_separator=':' link_all_deplibs=yes export_dynamic_flag_spec='$wl-Bexport' runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; *) ld_shlibs=no ;; esac if test sni = "$host_vendor"; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) export_dynamic_flag_spec='$wl-Blargedynsym' ;; esac fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 $as_echo "$ld_shlibs" >&6; } test no = "$ld_shlibs" && can_build_shared=no with_gnu_ld=$with_gnu_ld # # Do we need to explicitly link libc? # case "x$archive_cmds_need_lc" in x|xyes) # Assume -lc should be added archive_cmds_need_lc=yes if test yes,yes = "$GCC,$enable_shared"; then case $archive_cmds in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 $as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } if ${lt_cv_archive_cmds_need_lc+:} false; then : $as_echo_n "(cached) " >&6 else $RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$lt_prog_compiler_wl pic_flag=$lt_prog_compiler_pic compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$allow_undefined_flag allow_undefined_flag= if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then lt_cv_archive_cmds_need_lc=no else lt_cv_archive_cmds_need_lc=yes fi allow_undefined_flag=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5 $as_echo "$lt_cv_archive_cmds_need_lc" >&6; } archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc ;; esac fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 $as_echo_n "checking dynamic linker characteristics... " >&6; } if test yes = "$GCC"; then case $host_os in darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; *) lt_awk_arg='/^libraries:/' ;; esac case $host_os in mingw* | cegcc*) lt_sed_strip_eq='s|=\([A-Za-z]:\)|\1|g' ;; *) lt_sed_strip_eq='s|=/|/|g' ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` case $lt_search_path_spec in *\;*) # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` ;; *) lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` ;; esac # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary... lt_tmp_lt_search_path_spec= lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` # ...but if some path component already ends with the multilib dir we assume # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). case "$lt_multi_os_dir; $lt_search_path_spec " in "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) lt_multi_os_dir= ;; esac for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" elif test -n "$lt_multi_os_dir"; then test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' BEGIN {RS = " "; FS = "/|\n";} { lt_foo = ""; lt_count = 0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo = "/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[lt_foo]++; } if (lt_freq[lt_foo] == 1) { print lt_foo; } }'` # AWK program above erroneously prepends '/' to C:/dos/paths # for these hosts. case $host_os in mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ $SED 's|/\([A-Za-z]:\)|\1|g'` ;; esac sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=.so postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='$libname$release$shared_ext$major' ;; aix[4-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test ia64 = "$host_cpu"; then # AIX 5 supports IA64 library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line '#! .'. This would cause the generated library to # depend on '.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[01] | aix4.[01].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # Using Import Files as archive members, it is possible to support # filename-based versioning of shared library archives on AIX. While # this would work for both with and without runtime linking, it will # prevent static linking of such archives. So we do filename-based # shared library versioning with .so extension only, which is used # when both runtime linking and shared linking is enabled. # Unfortunately, runtime linking may impact performance, so we do # not want this to be the default eventually. Also, we use the # versioned .so libs for executables only if there is the -brtl # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. # To allow for filename-based versioning support, we need to create # libNAME.so.V as an archive file, containing: # *) an Import File, referring to the versioned filename of the # archive as well as the shared archive member, telling the # bitwidth (32 or 64) of that shared object, and providing the # list of exported symbols of that shared object, eventually # decorated with the 'weak' keyword # *) the shared object with the F_LOADONLY flag set, to really avoid # it being seen by the linker. # At run time we better use the real file rather than another symlink, # but for link time we create the symlink libNAME.so -> libNAME.so.V case $with_aix_soname,$aix_use_runtimelinking in # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. aix,yes) # traditional libtool dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; aix,no) # traditional AIX only dynamic_linker='AIX lib.a(lib.so.V)' # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' ;; svr4,*) # full svr4 only dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,yes) # both, prefer svr4 dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # unpreferred sharedlib libNAME.a needs extra handling postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,no) # both, prefer aix dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' ;; esac shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='$libname$shared_ext' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[45]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api" ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl* | *,icl*) # Native MSVC or ICC libname_spec='$name' soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' library_names_spec='$libname.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec=$LIB if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC and ICC wrapper library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' soname_spec='$libname$release$major$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib" sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[23].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[01]* | freebsdelf3.[01]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=no sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' if test 32 = "$HPUX_IA64_MODE"; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[3-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test yes = "$lt_cv_prog_gnu_ld"; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; linux*android*) version_type=none # Android doesn't support versioned libraries. need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext' soname_spec='$libname$release$shared_ext' finish_cmds= shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes dynamic_linker='Android linker' # Don't embed -rpath directories since the linker doesn't support them. hardcode_libdir_flag_spec='-L$libdir' ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH if ${lt_cv_shlibpath_overrides_runpath+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : lt_cv_shlibpath_overrides_runpath=yes fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS libdir=$save_libdir fi shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, # even though it is searched at run-time. Try to do the best guess by # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd* | bitrig*) version_type=sunos sys_lib_dlsearch_path_spec=/usr/lib need_lib_prefix=no if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then need_version=no else need_version=yes fi library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; os2*) libname_spec='$name' version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no # OS/2 can only load a DLL with a base name of 8 characters or less. soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; v=$($ECHO $release$versuffix | tr -d .-); n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); $ECHO $n$v`$shared_ext' library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' shlibpath_var=BEGINLIBPATH sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test yes = "$with_gnu_ld"; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec; then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' soname_spec='$libname$shared_ext.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=sco need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test yes = "$with_gnu_ld"; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 $as_echo "$dynamic_linker" >&6; } test no = "$dynamic_linker" && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test yes = "$GCC"; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec fi if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi # remember unaugmented sys_lib_dlsearch_path content for libtool script decls... configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec # ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" # to be used as default LT_SYS_LIBRARY_PATH value in generated libtool configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 $as_echo_n "checking how to hardcode library paths into programs... " >&6; } hardcode_action= if test -n "$hardcode_libdir_flag_spec" || test -n "$runpath_var" || test yes = "$hardcode_automatic"; then # We can hardcode non-existent directories. if test no != "$hardcode_direct" && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, )" && test no != "$hardcode_minus_L"; then # Linking always hardcodes the temporary library directory. hardcode_action=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. hardcode_action=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. hardcode_action=unsupported fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 $as_echo "$hardcode_action" >&6; } if test relink = "$hardcode_action" || test yes = "$inherit_rpath"; then # Fast installation is not supported enable_fast_install=no elif test yes = "$shlibpath_overrides_runpath" || test no = "$enable_shared"; then # Fast installation is not necessary enable_fast_install=needless fi if test yes != "$enable_dlopen"; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen=load_add_on lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen=LoadLibrary lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen=dlopen lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 $as_echo_n "checking for dlopen in -ldl... " >&6; } if ${ac_cv_lib_dl_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dl_dlopen=yes else ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes; then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl else lt_cv_dlopen=dyld lt_cv_dlopen_libs= lt_cv_dlopen_self=yes fi ;; tpf*) # Don't try to run any link tests for TPF. We know it's impossible # because TPF is a cross-compiler, and we know how we open DSOs. lt_cv_dlopen=dlopen lt_cv_dlopen_libs= lt_cv_dlopen_self=no ;; *) ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" if test "x$ac_cv_func_shl_load" = xyes; then : lt_cv_dlopen=shl_load else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 $as_echo_n "checking for shl_load in -ldld... " >&6; } if ${ac_cv_lib_dld_shl_load+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char shl_load (); int main () { return shl_load (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dld_shl_load=yes else ac_cv_lib_dld_shl_load=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 $as_echo "$ac_cv_lib_dld_shl_load" >&6; } if test "x$ac_cv_lib_dld_shl_load" = xyes; then : lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld else ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" if test "x$ac_cv_func_dlopen" = xyes; then : lt_cv_dlopen=dlopen else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 $as_echo_n "checking for dlopen in -ldl... " >&6; } if ${ac_cv_lib_dl_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dl_dlopen=yes else ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes; then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 $as_echo_n "checking for dlopen in -lsvld... " >&6; } if ${ac_cv_lib_svld_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsvld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_svld_dlopen=yes else ac_cv_lib_svld_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 $as_echo "$ac_cv_lib_svld_dlopen" >&6; } if test "x$ac_cv_lib_svld_dlopen" = xyes; then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 $as_echo_n "checking for dld_link in -ldld... " >&6; } if ${ac_cv_lib_dld_dld_link+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dld_link (); int main () { return dld_link (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dld_dld_link=yes else ac_cv_lib_dld_dld_link=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 $as_echo "$ac_cv_lib_dld_dld_link" >&6; } if test "x$ac_cv_lib_dld_dld_link" = xyes; then : lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld fi fi fi fi fi fi ;; esac if test no = "$lt_cv_dlopen"; then enable_dlopen=no else enable_dlopen=yes fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS=$CPPFLAGS test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS=$LDFLAGS wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS=$LIBS LIBS="$lt_cv_dlopen_libs $LIBS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 $as_echo_n "checking whether a program can dlopen itself... " >&6; } if ${lt_cv_dlopen_self+:} false; then : $as_echo_n "(cached) " >&6 else if test yes = "$cross_compiling"; then : lt_cv_dlopen_self=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; esac else : # compilation failed lt_cv_dlopen_self=no fi fi rm -fr conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 $as_echo "$lt_cv_dlopen_self" >&6; } if test yes = "$lt_cv_dlopen_self"; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 $as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; } if ${lt_cv_dlopen_self_static+:} false; then : $as_echo_n "(cached) " >&6 else if test yes = "$cross_compiling"; then : lt_cv_dlopen_self_static=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; esac else : # compilation failed lt_cv_dlopen_self_static=no fi fi rm -fr conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 $as_echo "$lt_cv_dlopen_self_static" >&6; } fi CPPFLAGS=$save_CPPFLAGS LDFLAGS=$save_LDFLAGS LIBS=$save_LIBS ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi striplib= old_striplib= { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 $as_echo_n "checking whether stripping libraries is possible... " >&6; } if test -z "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } else if $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then old_striplib="$STRIP --strip-debug" striplib="$STRIP --strip-unneeded" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else case $host_os in darwin*) # FIXME - insert some real tests, host_os isn't really good enough striplib="$STRIP -x" old_striplib="$STRIP -S" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } ;; freebsd*) if $STRIP -V 2>&1 | $GREP "elftoolchain" >/dev/null; then old_striplib="$STRIP --strip-debug" striplib="$STRIP --strip-unneeded" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; esac fi fi # Report what library types will actually be built { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 $as_echo_n "checking if libtool supports shared libraries... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 $as_echo "$can_build_shared" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 $as_echo_n "checking whether to build shared libraries... " >&6; } test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[4-9]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 $as_echo "$enable_shared" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 $as_echo_n "checking whether to build static libraries... " >&6; } # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 $as_echo "$enable_static" >&6; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu CC=$lt_save_CC ac_config_commands="$ac_config_commands libtool" # Only expand once: ac_config_headers="$ac_config_headers config.h" ac_config_files="$ac_config_files Makefile doc/Doxyfile wimlib.pc" ac_config_files="$ac_config_files programs/mkwinpeimg" PKGCONFIG_PRIVATE_REQUIRES="" PKGCONFIG_PRIVATE_LIBS="" ############################################################################### # General platform features # ############################################################################### ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 $as_echo_n "checking whether $CC understands -c and -o together... " >&6; } if ${am_cv_prog_cc_c_o+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 $as_echo "$am_cv_prog_cc_c_o" >&6; } if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu depcc="$CC" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if ${am_cv_CC_dependencies_compiler_type+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CC_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CC_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CC_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 $as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then am__fastdepCC_TRUE= am__fastdepCC_FALSE='#' else am__fastdepCC_TRUE='#' am__fastdepCC_FALSE= fi WINDOWS_NATIVE_BUILD="no" PLATFORM_CPPFLAGS="" PLATFORM_CFLAGS="-fvisibility=hidden" PLATFORM_LDFLAGS="" case "$host_os" in mingw*) # Native Windows WINDOWS_NATIVE_BUILD="yes" # -D__MINGW_USE_VC2005_COMPAT: make time_t 64-bit on 32-bit Windows. PLATFORM_CPPFLAGS="-D_POSIX -D_POSIX_THREAD_SAFE_FUNCTIONS -DUNICODE -D_UNICODE -D_CRT_NON_CONFORMING_SWPRINTFS -D__MINGW_USE_VC2005_COMPAT" PLATFORM_CFLAGS="-municode -mno-ms-bitfields" PLATFORM_LDFLAGS="-no-undefined" WITH_NTFS_3G_DEFAULT="no" WITH_FUSE_DEFAULT="no" ;; linux*) # Linux WITH_NTFS_3G_DEFAULT="yes" WITH_FUSE_DEFAULT="yes" ;; *) # Other UNIX WITH_NTFS_3G_DEFAULT="yes" WITH_FUSE_DEFAULT="no" ;; esac PLATFORM_CPPFLAGS=$PLATFORM_CPPFLAGS PLATFORM_CFLAGS=$PLATFORM_CFLAGS PLATFORM_LDFLAGS=$PLATFORM_LDFLAGS if test "$WINDOWS_NATIVE_BUILD" = "yes"; then WINDOWS_NATIVE_BUILD_TRUE= WINDOWS_NATIVE_BUILD_FALSE='#' else WINDOWS_NATIVE_BUILD_TRUE='#' WINDOWS_NATIVE_BUILD_FALSE= fi # Useful functions which we can do without. for ac_func in futimens utimensat flock mempcpy \ openat fstatat readlinkat fdopendir posix_fallocate \ llistxattr lgetxattr fsetxattr lsetxattr getopt_long_only do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done # Header checks, most of which are only here to satisfy conditional includes # made by the libntfs-3g headers. for ac_header in alloca.h \ byteswap.h \ endian.h \ errno.h \ glob.h \ machine/endian.h \ stdarg.h \ stddef.h \ stdlib.h \ sys/byteorder.h \ sys/endian.h \ sys/file.h \ sys/syscall.h \ sys/sysctl.h \ sys/times.h \ sys/xattr.h \ time.h \ utime.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done # Does stat() support nanosecond-precision timestamps? (This is relevant on # UNIX but not on Windows.) ac_fn_c_check_member "$LINENO" "struct stat" "st_mtim" "ac_cv_member_struct_stat_st_mtim" "#include " if test "x$ac_cv_member_struct_stat_st_mtim" = xyes; then : $as_echo "#define HAVE_STAT_NANOSECOND_PRECISION 1" >>confdefs.h fi # Check for possible support for the Linux getrandom() system call ac_fn_c_check_decl "$LINENO" "__NR_getrandom" "ac_cv_have_decl___NR_getrandom" "#include " if test "x$ac_cv_have_decl___NR_getrandom" = xyes; then : $as_echo "#define HAVE_NR_GETRANDOM 1" >>confdefs.h fi ############################################################################### # Required libraries # ############################################################################### # ------------------------------ pthreads ------------------------------------- ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ax_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on True64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS" >&5 $as_echo_n "checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pthread_join (); int main () { return pthread_join (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ax_pthread_ok=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5 $as_echo "$ax_pthread_ok" >&6; } if test x"$ax_pthread_ok" = xno; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) # -pthreads: Solaris/gcc # -mthreads: Mingw32/gcc, Lynx/gcc # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads too; # also defines -D_REENTRANT) # ... -mt is also the pthreads flag for HP/aCC # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case ${host_os} in solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (We need to link with -pthreads/-mt/ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather # a function called by this macro, so we could check for that, but # who knows whether they'll stub that too in a future libc.) So, # we'll just look for -pthreads and -lpthread first: ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" ;; darwin*) ax_pthread_flags="-pthread $ax_pthread_flags" ;; esac # Clang doesn't consider unrecognized options an error unless we specify # -Werror. We throw in some extra Clang-specific options to ensure that # this doesn't happen for GCC, which also accepts -Werror. { $as_echo "$as_me:${as_lineno-$LINENO}: checking if compiler needs -Werror to reject unknown flags" >&5 $as_echo_n "checking if compiler needs -Werror to reject unknown flags... " >&6; } save_CFLAGS="$CFLAGS" ax_pthread_extra_flags="-Werror" CFLAGS="$CFLAGS $ax_pthread_extra_flags -Wunknown-warning-option -Wsizeof-array-argument" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int foo(void); int main () { foo() ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else ax_pthread_extra_flags= { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$save_CFLAGS" if test x"$ax_pthread_ok" = xno; then for flag in $ax_pthread_flags; do case $flag in none) { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work without any flags" >&5 $as_echo_n "checking whether pthreads work without any flags... " >&6; } ;; -*) { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with $flag" >&5 $as_echo_n "checking whether pthreads work with $flag... " >&6; } PTHREAD_CFLAGS="$flag" ;; pthread-config) # Extract the first word of "pthread-config", so it can be a program name with args. set dummy pthread-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ax_pthread_config+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ax_pthread_config"; then ac_cv_prog_ax_pthread_config="$ax_pthread_config" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ax_pthread_config="yes" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_prog_ax_pthread_config" && ac_cv_prog_ax_pthread_config="no" fi fi ax_pthread_config=$ac_cv_prog_ax_pthread_config if test -n "$ax_pthread_config"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_config" >&5 $as_echo "$ax_pthread_config" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test x"$ax_pthread_config" = xno; then continue; fi PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the pthreads library -l$flag" >&5 $as_echo_n "checking for the pthreads library -l$flag... " >&6; } PTHREAD_LIBS="-l$flag" ;; esac save_LIBS="$LIBS" save_CFLAGS="$CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS $ax_pthread_extra_flags" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include static void routine(void *a) { a = 0; } static void *start_routine(void *a) { return a; } int main () { pthread_t th; pthread_attr_t attr; pthread_create(&th, 0, start_routine, 0); pthread_join(th, 0); pthread_attr_init(&attr); pthread_cleanup_push(routine, 0); pthread_cleanup_pop(0) /* ; */ ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ax_pthread_ok=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5 $as_echo "$ax_pthread_ok" >&6; } if test "x$ax_pthread_ok" = xyes; then break; fi PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$ax_pthread_ok" = xyes; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for joinable pthread attribute" >&5 $as_echo_n "checking for joinable pthread attribute... " >&6; } attr_name=unknown for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { int attr = $attr; return attr /* ; */ ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : attr_name=$attr; break fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext done { $as_echo "$as_me:${as_lineno-$LINENO}: result: $attr_name" >&5 $as_echo "$attr_name" >&6; } if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then cat >>confdefs.h <<_ACEOF #define PTHREAD_CREATE_JOINABLE $attr_name _ACEOF fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if more special flags are required for pthreads" >&5 $as_echo_n "checking if more special flags are required for pthreads... " >&6; } flag=no case ${host_os} in aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";; osf* | hpux*) flag="-D_REENTRANT";; solaris*) if test "$GCC" = "yes"; then flag="-D_REENTRANT" else # TODO: What about Clang on Solaris? flag="-mt -D_REENTRANT" fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $flag" >&5 $as_echo "$flag" >&6; } if test "x$flag" != xno; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for PTHREAD_PRIO_INHERIT" >&5 $as_echo_n "checking for PTHREAD_PRIO_INHERIT... " >&6; } if ${ax_cv_PTHREAD_PRIO_INHERIT+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { int i = PTHREAD_PRIO_INHERIT; ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ax_cv_PTHREAD_PRIO_INHERIT=yes else ax_cv_PTHREAD_PRIO_INHERIT=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_PRIO_INHERIT" >&5 $as_echo "$ax_cv_PTHREAD_PRIO_INHERIT" >&6; } if test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"; then : $as_echo "#define HAVE_PTHREAD_PRIO_INHERIT 1" >>confdefs.h fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" # More AIX lossage: compile with *_r variant if test "x$GCC" != xyes; then case $host_os in aix*) case "x/$CC" in #( x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6) : #handle absolute path differently from PATH based program lookup case "x$CC" in #( x/*) : if as_fn_executable_p ${CC}_r; then : PTHREAD_CC="${CC}_r" fi ;; #( *) : for ac_prog in ${CC}_r do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_PTHREAD_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$PTHREAD_CC"; then ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_PTHREAD_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi PTHREAD_CC=$ac_cv_prog_PTHREAD_CC if test -n "$PTHREAD_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PTHREAD_CC" >&5 $as_echo "$PTHREAD_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$PTHREAD_CC" && break done test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" ;; esac ;; #( *) : ;; esac ;; esac fi fi test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test x"$ax_pthread_ok" = xyes; then $as_echo "#define HAVE_PTHREAD 1" >>confdefs.h : else ax_pthread_ok=no as_fn_error $? "\"cannot find pthreads library\"" "$LINENO" 5 fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # ------------------------------ libxml2 -------------------------------------- if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 $as_echo "$PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_PKG_CONFIG"; then ac_pt_PKG_CONFIG=$PKG_CONFIG # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG if test -n "$ac_pt_PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 $as_echo "$ac_pt_PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_PKG_CONFIG" = x; then PKG_CONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac PKG_CONFIG=$ac_pt_PKG_CONFIG fi else PKG_CONFIG="$ac_cv_path_PKG_CONFIG" fi fi if test -n "$PKG_CONFIG"; then _pkg_min_version=0.9.0 { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 $as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } PKG_CONFIG="" fi fi pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBXML2" >&5 $as_echo_n "checking for LIBXML2... " >&6; } if test -n "$LIBXML2_CFLAGS"; then pkg_cv_LIBXML2_CFLAGS="$LIBXML2_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libxml-2.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libxml-2.0") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBXML2_CFLAGS=`$PKG_CONFIG --cflags "libxml-2.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBXML2_LIBS"; then pkg_cv_LIBXML2_LIBS="$LIBXML2_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libxml-2.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libxml-2.0") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBXML2_LIBS=`$PKG_CONFIG --libs "libxml-2.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBXML2_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libxml-2.0" 2>&1` else LIBXML2_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libxml-2.0" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBXML2_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (libxml-2.0) were not met: $LIBXML2_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables LIBXML2_CFLAGS and LIBXML2_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables LIBXML2_CFLAGS and LIBXML2_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else LIBXML2_CFLAGS=$pkg_cv_LIBXML2_CFLAGS LIBXML2_LIBS=$pkg_cv_LIBXML2_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi PKGCONFIG_PRIVATE_REQUIRES="$PKGCONFIG_PRIVATE_REQUIRES libxml-2.0" ############################################################################### # Configuration options # ############################################################################### # ------------------------- ntfs-3g support ----------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to include support for ntfs-3g" >&5 $as_echo_n "checking whether to include support for ntfs-3g... " >&6; } # Check whether --with-ntfs-3g was given. if test "${with_ntfs_3g+set}" = set; then : withval=$with_ntfs_3g; WITH_NTFS_3G=$withval else WITH_NTFS_3G=$WITH_NTFS_3G_DEFAULT fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $WITH_NTFS_3G" >&5 $as_echo "$WITH_NTFS_3G" >&6; } if test "$WITH_NTFS_3G" = "yes"; then pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBNTFS_3G" >&5 $as_echo_n "checking for LIBNTFS_3G... " >&6; } if test -n "$LIBNTFS_3G_CFLAGS"; then pkg_cv_LIBNTFS_3G_CFLAGS="$LIBNTFS_3G_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libntfs-3g >= 2011.4.12\""; } >&5 ($PKG_CONFIG --exists --print-errors "libntfs-3g >= 2011.4.12") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBNTFS_3G_CFLAGS=`$PKG_CONFIG --cflags "libntfs-3g >= 2011.4.12" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBNTFS_3G_LIBS"; then pkg_cv_LIBNTFS_3G_LIBS="$LIBNTFS_3G_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libntfs-3g >= 2011.4.12\""; } >&5 ($PKG_CONFIG --exists --print-errors "libntfs-3g >= 2011.4.12") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBNTFS_3G_LIBS=`$PKG_CONFIG --libs "libntfs-3g >= 2011.4.12" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBNTFS_3G_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libntfs-3g >= 2011.4.12" 2>&1` else LIBNTFS_3G_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libntfs-3g >= 2011.4.12" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBNTFS_3G_PKG_ERRORS" >&5 as_fn_error $? "Cannot find libntfs-3g version 2011-4-12 or later! Without libntfs-3g, wimlib cannot include support for capturing or applying a WIM image directly from/to an unmounted NTFS volume while preserving NTFS-specific data such as security descriptors and named data streams. Either install libntfs-3g, or configure --without-ntfs-3g to disable this feature." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } as_fn_error $? "Cannot find libntfs-3g version 2011-4-12 or later! Without libntfs-3g, wimlib cannot include support for capturing or applying a WIM image directly from/to an unmounted NTFS volume while preserving NTFS-specific data such as security descriptors and named data streams. Either install libntfs-3g, or configure --without-ntfs-3g to disable this feature." "$LINENO" 5 else LIBNTFS_3G_CFLAGS=$pkg_cv_LIBNTFS_3G_CFLAGS LIBNTFS_3G_LIBS=$pkg_cv_LIBNTFS_3G_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi PKGCONFIG_PRIVATE_REQUIRES="$PKGCONFIG_PRIVATE_REQUIRES libntfs-3g" $as_echo "#define WITH_NTFS_3G 1" >>confdefs.h fi if test "$WITH_NTFS_3G" = "yes"; then WITH_NTFS_3G_TRUE= WITH_NTFS_3G_FALSE='#' else WITH_NTFS_3G_TRUE='#' WITH_NTFS_3G_FALSE= fi # ------------------------ FUSE mount support --------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to include support for mounting WIMs" >&5 $as_echo_n "checking whether to include support for mounting WIMs... " >&6; } # Check whether --with-fuse was given. if test "${with_fuse+set}" = set; then : withval=$with_fuse; WITH_FUSE=$withval else WITH_FUSE=$WITH_FUSE_DEFAULT fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $WITH_FUSE" >&5 $as_echo "$WITH_FUSE" >&6; } if test "$WITH_FUSE" = "yes"; then pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBFUSE" >&5 $as_echo_n "checking for LIBFUSE... " >&6; } if test -n "$LIBFUSE_CFLAGS"; then pkg_cv_LIBFUSE_CFLAGS="$LIBFUSE_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"fuse\""; } >&5 ($PKG_CONFIG --exists --print-errors "fuse") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBFUSE_CFLAGS=`$PKG_CONFIG --cflags "fuse" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBFUSE_LIBS"; then pkg_cv_LIBFUSE_LIBS="$LIBFUSE_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"fuse\""; } >&5 ($PKG_CONFIG --exists --print-errors "fuse") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBFUSE_LIBS=`$PKG_CONFIG --libs "fuse" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBFUSE_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "fuse" 2>&1` else LIBFUSE_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "fuse" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBFUSE_PKG_ERRORS" >&5 as_fn_error $? "Cannot find libfuse! Without libfuse, wimlib cannot include support for mounting WIM images. Either install libfuse, or configure --without-fuse to disable this feature." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } as_fn_error $? "Cannot find libfuse! Without libfuse, wimlib cannot include support for mounting WIM images. Either install libfuse, or configure --without-fuse to disable this feature." "$LINENO" 5 else LIBFUSE_CFLAGS=$pkg_cv_LIBFUSE_CFLAGS LIBFUSE_LIBS=$pkg_cv_LIBFUSE_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi PKGCONFIG_PRIVATE_REQUIRES="$PKGCONFIG_PRIVATE_REQUIRES fuse" $as_echo "#define WITH_FUSE 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: checking for mq_open in -lrt" >&5 $as_echo_n "checking for mq_open in -lrt... " >&6; } if ${ac_cv_lib_rt_mq_open+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lrt $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char mq_open (); int main () { return mq_open (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_rt_mq_open=yes else ac_cv_lib_rt_mq_open=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_mq_open" >&5 $as_echo "$ac_cv_lib_rt_mq_open" >&6; } if test "x$ac_cv_lib_rt_mq_open" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBRT 1 _ACEOF LIBS="-lrt $LIBS" else as_fn_error $? "Cannot find librt (the POSIX.1b Realtime Extensions Library)! wimlib needs this for the POSIX message queue functions, which are used in the code for mounting WIM images. Recent versions of glibc include this library. Either install this library, or configure --without-fuse to disable support for mounting WIM images." "$LINENO" 5 fi PKGCONFIG_PRIVATE_LIBS="$PKGCONFIG_PRIVATE_LIBS -lrt" LIBRT_LIBS=-lrt fi if test "$WITH_FUSE" = "yes"; then WITH_FUSE_TRUE= WITH_FUSE_FALSE='#' else WITH_FUSE_TRUE='#' WITH_FUSE_FALSE= fi # ------------------------ SHA-1 implementation --------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use SSSE3-accelerated SHA-1" >&5 $as_echo_n "checking whether to use SSSE3-accelerated SHA-1... " >&6; } # Check whether --enable-ssse3-sha1 was given. if test "${enable_ssse3_sha1+set}" = set; then : enableval=$enable_ssse3_sha1; ENABLE_SSSE3_SHA1=$enableval else ENABLE_SSSE3_SHA1=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ENABLE_SSSE3_SHA1" >&5 $as_echo "$ENABLE_SSSE3_SHA1" >&6; } if test "$ENABLE_SSSE3_SHA1" = "yes" ; then $as_echo "#define ENABLE_SSSE3_SHA1 1" >>confdefs.h for ac_prog in nasm nasmw yasm do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_NASM+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$NASM"; then ac_cv_prog_NASM="$NASM" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_NASM="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi NASM=$ac_cv_prog_NASM if test -n "$NASM"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NASM" >&5 $as_echo "$NASM" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$NASM" && break done test -z "$NASM" && as_fn_error $? "no nasm (Netwide Assembler) found" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for object file format of host system" >&5 $as_echo_n "checking for object file format of host system... " >&6; } case "$host_os" in cygwin* | mingw* | pw32* | interix*) case "$host_cpu" in x86_64) objfmt='Win64-COFF' ;; *) objfmt='Win32-COFF' ;; esac ;; msdosdjgpp* | go32*) objfmt='COFF' ;; os2-emx*) # not tested objfmt='MSOMF' # obj ;; linux*coff* | linux*oldld*) objfmt='COFF' # ??? ;; linux*aout*) objfmt='a.out' ;; linux*) case "$host_cpu" in x86_64) objfmt='ELF64' ;; *) objfmt='ELF' ;; esac ;; kfreebsd* | freebsd* | netbsd* | openbsd*) if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then objfmt='BSD-a.out' else case "$host_cpu" in x86_64 | amd64) objfmt='ELF64' ;; *) objfmt='ELF' ;; esac fi ;; solaris* | sunos* | sysv* | sco*) case "$host_cpu" in x86_64) objfmt='ELF64' ;; *) objfmt='ELF' ;; esac ;; darwin* | rhapsody* | nextstep* | openstep* | macos*) case "$host_cpu" in x86_64) objfmt='Mach-O64' ;; *) objfmt='Mach-O' ;; esac ;; *) objfmt='ELF ?' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $objfmt" >&5 $as_echo "$objfmt" >&6; } if test "$objfmt" = 'ELF ?'; then objfmt='ELF' { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unexpected host system. assumed that the format is $objfmt." >&5 $as_echo "$as_me: WARNING: unexpected host system. assumed that the format is $objfmt." >&2;} fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for object file format specifier (NAFLAGS) " >&5 $as_echo_n "checking for object file format specifier (NAFLAGS) ... " >&6; } case "$objfmt" in MSOMF) NAFLAGS='-fobj -DOBJ32';; Win32-COFF) NAFLAGS='-fwin32 -DWIN32';; Win64-COFF) NAFLAGS='-fwin64 -DWIN64 -D__x86_64__';; COFF) NAFLAGS='-fcoff -DCOFF';; a.out) NAFLAGS='-faout -DAOUT';; BSD-a.out) NAFLAGS='-faoutb -DAOUT';; ELF) NAFLAGS='-felf -DELF';; ELF64) NAFLAGS='-felf64 -DELF -D__x86_64__';; RDF) NAFLAGS='-frdf -DRDF';; Mach-O) NAFLAGS='-fmacho -DMACHO';; Mach-O64) NAFLAGS='-fmacho64 -DMACHO -D__x86_64__';; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NAFLAGS" >&5 $as_echo "$NAFLAGS" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the assembler ($NASM $NAFLAGS) works" >&5 $as_echo_n "checking whether the assembler ($NASM $NAFLAGS) works... " >&6; } cat > conftest.asm <&5 (eval $try_nasm) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s conftest.o; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else echo "configure: failed program was:" >&5 cat conftest.asm >&5 rm -rf conftest* { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } as_fn_error $? "installation or configuration problem: assembler cannot create object files." "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the linker accepts assembler output" >&5 $as_echo_n "checking whether the linker accepts assembler output... " >&6; } try_nasm='${CC-cc} -o conftest${ac_exeext} $LDFLAGS conftest.o $LIBS 1>&5' if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$try_nasm\""; } >&5 (eval $try_nasm) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s conftest${ac_exeext}; then rm -rf conftest* { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else rm -rf conftest* { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } as_fn_error $? "configuration problem: maybe object file format mismatch." "$LINENO" 5 fi NASM_SYMBOL_PREFIX="" NASM_PLATFORM_FLAGS="" if test "$WINDOWS_NATIVE_BUILD" = "yes"; then NASM_PLATFORM_FLAGS="-DWIN_ABI" fi case "$host_os" in darwin* | rhapsody* | nextstep* | openstep* | macos*) NASM_SYMBOL_PREFIX="_" ;; esac NASM_PLATFORM_FLAGS=$NASM_PLATFORM_FLAGS NASM_SYMBOL_PREFIX=$NASM_SYMBOL_PREFIX else { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use SHA-1 implementation from system libcrypto" >&5 $as_echo_n "checking whether to use SHA-1 implementation from system libcrypto... " >&6; } # Check whether --with-libcrypto was given. if test "${with_libcrypto+set}" = set; then : withval=$with_libcrypto; WITH_LIBCRYPTO=$withval else WITH_LIBCRYPTO=auto fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $WITH_LIBCRYPTO" >&5 $as_echo "$WITH_LIBCRYPTO" >&6; } if test "$WITH_LIBCRYPTO" != "no"; then pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBCRYPTO" >&5 $as_echo_n "checking for LIBCRYPTO... " >&6; } if test -n "$LIBCRYPTO_CFLAGS"; then pkg_cv_LIBCRYPTO_CFLAGS="$LIBCRYPTO_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libcrypto\""; } >&5 ($PKG_CONFIG --exists --print-errors "libcrypto") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBCRYPTO_CFLAGS=`$PKG_CONFIG --cflags "libcrypto" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBCRYPTO_LIBS"; then pkg_cv_LIBCRYPTO_LIBS="$LIBCRYPTO_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libcrypto\""; } >&5 ($PKG_CONFIG --exists --print-errors "libcrypto") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBCRYPTO_LIBS=`$PKG_CONFIG --libs "libcrypto" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBCRYPTO_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libcrypto" 2>&1` else LIBCRYPTO_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libcrypto" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBCRYPTO_PKG_ERRORS" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Cannot find libcrypto: using stand-alone SHA-1 code instead" >&5 $as_echo "$as_me: WARNING: Cannot find libcrypto: using stand-alone SHA-1 code instead" >&2;} elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Cannot find libcrypto: using stand-alone SHA-1 code instead" >&5 $as_echo "$as_me: WARNING: Cannot find libcrypto: using stand-alone SHA-1 code instead" >&2;} else LIBCRYPTO_CFLAGS=$pkg_cv_LIBCRYPTO_CFLAGS LIBCRYPTO_LIBS=$pkg_cv_LIBCRYPTO_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } PKGCONFIG_PRIVATE_REQUIRES="$PKGCONFIG_PRIVATE_REQUIRES libcrypto" $as_echo "#define WITH_LIBCRYPTO 1" >>confdefs.h fi fi fi if test "$ENABLE_SSSE3_SHA1" = "yes"; then ENABLE_SSSE3_SHA1_TRUE= ENABLE_SSSE3_SHA1_FALSE='#' else ENABLE_SSSE3_SHA1_TRUE='#' ENABLE_SSSE3_SHA1_FALSE= fi # ----------------------------- Other options --------------------------------- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to include error messages" >&5 $as_echo_n "checking whether to include error messages... " >&6; } # Check whether --enable-error_messages was given. if test "${enable_error_messages+set}" = set; then : enableval=$enable_error_messages; ENABLE_ERROR_MESSAGES=$enableval else ENABLE_ERROR_MESSAGES=yes fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ENABLE_ERROR_MESSAGES" >&5 $as_echo "$ENABLE_ERROR_MESSAGES" >&6; } if test "$ENABLE_ERROR_MESSAGES" = "yes"; then $as_echo "#define ENABLE_ERROR_MESSAGES 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to include assertions" >&5 $as_echo_n "checking whether to include assertions... " >&6; } # Check whether --enable-assertions was given. if test "${enable_assertions+set}" = set; then : enableval=$enable_assertions; ENABLE_ASSERTIONS=$enableval else ENABLE_ASSERTIONS=yes fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ENABLE_ASSERTIONS" >&5 $as_echo "$ENABLE_ASSERTIONS" >&6; } if test "$ENABLE_ASSERTIONS" = "yes"; then $as_echo "#define ENABLE_ASSERTIONS 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to include support for multi-threaded compression" >&5 $as_echo_n "checking whether to include support for multi-threaded compression... " >&6; } # Check whether --enable-multithreaded-compression was given. if test "${enable_multithreaded_compression+set}" = set; then : enableval=$enable_multithreaded_compression; ENABLE_MULTITHREADED_COMPRESSION=$enableval else ENABLE_MULTITHREADED_COMPRESSION=yes fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ENABLE_MULTITHREADED_COMPRESSION" >&5 $as_echo "$ENABLE_MULTITHREADED_COMPRESSION" >&6; } if test "$ENABLE_MULTITHREADED_COMPRESSION" = "yes"; then $as_echo "#define ENABLE_MULTITHREADED_COMPRESSION 1" >>confdefs.h fi # Check whether --with-pkgconfigdir was given. if test "${with_pkgconfigdir+set}" = set; then : withval=$with_pkgconfigdir; pkgconfigdir=$withval else pkgconfigdir='${libdir}/pkgconfig' fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable supporting code for tests" >&5 $as_echo_n "checking whether to enable supporting code for tests... " >&6; } # Check whether --enable-test-support was given. if test "${enable_test_support+set}" = set; then : enableval=$enable_test_support; ENABLE_TEST_SUPPORT=$enableval else ENABLE_TEST_SUPPORT=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ENABLE_TEST_SUPPORT" >&5 $as_echo "$ENABLE_TEST_SUPPORT" >&6; } if test "$ENABLE_TEST_SUPPORT" = "yes" ; then $as_echo "#define ENABLE_TEST_SUPPORT 1" >>confdefs.h fi if test "$ENABLE_TEST_SUPPORT" = "yes"; then ENABLE_TEST_SUPPORT_TRUE= ENABLE_TEST_SUPPORT_FALSE='#' else ENABLE_TEST_SUPPORT_TRUE='#' ENABLE_TEST_SUPPORT_FALSE= fi ############################################################################### PKGCONFIG_PRIVATE_REQUIRES=$PKGCONFIG_PRIVATE_REQUIRES PKGCONFIG_PRIVATE_LIBS=$PKGCONFIG_PRIVATE_LIBS cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs { $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 $as_echo_n "checking that generated files are newer than configure... " >&6; } if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 $as_echo "done" >&6; } if test -n "$EXEEXT"; then am__EXEEXT_TRUE= am__EXEEXT_FALSE='#' else am__EXEEXT_TRUE='#' am__EXEEXT_FALSE= fi if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then as_fn_error $? "conditional \"AMDEP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${WINDOWS_NATIVE_BUILD_TRUE}" && test -z "${WINDOWS_NATIVE_BUILD_FALSE}"; then as_fn_error $? "conditional \"WINDOWS_NATIVE_BUILD\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${WITH_NTFS_3G_TRUE}" && test -z "${WITH_NTFS_3G_FALSE}"; then as_fn_error $? "conditional \"WITH_NTFS_3G\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${WITH_FUSE_TRUE}" && test -z "${WITH_FUSE_FALSE}"; then as_fn_error $? "conditional \"WITH_FUSE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_SSSE3_SHA1_TRUE}" && test -z "${ENABLE_SSSE3_SHA1_FALSE}"; then as_fn_error $? "conditional \"ENABLE_SSSE3_SHA1\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_TEST_SUPPORT_TRUE}" && test -z "${ENABLE_TEST_SUPPORT_FALSE}"; then as_fn_error $? "conditional \"ENABLE_TEST_SUPPORT\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by wimlib $as_me 1.13.1, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" config_commands="$ac_config_commands" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Configuration commands: $config_commands Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ wimlib config.status 1.13.1 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' MKDIR_P='$MKDIR_P' AWK='$AWK' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # # INIT-COMMANDS # AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}" # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`' macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`' enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`' enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`' pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`' enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`' shared_archive_member_spec='`$ECHO "$shared_archive_member_spec" | $SED "$delay_single_quote_subst"`' SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`' ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`' PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`' host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`' host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`' host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`' build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`' build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`' build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`' SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`' Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`' GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`' EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`' FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`' LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`' NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`' LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`' max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`' ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`' exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`' lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`' lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`' lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`' lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`' lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`' reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`' reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`' OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`' deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`' file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`' file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`' want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`' DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`' sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`' AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' lt_ar_flags='`$ECHO "$lt_ar_flags" | $SED "$delay_single_quote_subst"`' AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`' STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`' old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`' old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`' old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`' lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`' CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`' CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`' compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`' GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_import='`$ECHO "$lt_cv_sys_global_symbol_to_import" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`' lt_cv_nm_interface='`$ECHO "$lt_cv_nm_interface" | $SED "$delay_single_quote_subst"`' nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`' lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`' lt_cv_truncate_bin='`$ECHO "$lt_cv_truncate_bin" | $SED "$delay_single_quote_subst"`' objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`' MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`' lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`' need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`' MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`' DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`' NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`' LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`' OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`' OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`' libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`' shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`' extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`' archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`' enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`' export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`' whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`' compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`' old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`' old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`' archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`' archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`' module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`' module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`' with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`' allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`' no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`' hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`' hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`' hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`' hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`' hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`' hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`' hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`' inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`' link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`' always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`' export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`' exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`' include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`' prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`' postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`' file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`' variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`' need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`' need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`' version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`' runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`' shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`' shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`' libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`' library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`' soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`' install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`' postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`' postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`' finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`' finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`' hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`' sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`' configure_time_dlsearch_path='`$ECHO "$configure_time_dlsearch_path" | $SED "$delay_single_quote_subst"`' configure_time_lt_sys_library_path='`$ECHO "$configure_time_lt_sys_library_path" | $SED "$delay_single_quote_subst"`' hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`' enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`' enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`' enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`' old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`' striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`' LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } # Quote evaled strings. for var in SHELL \ ECHO \ PATH_SEPARATOR \ SED \ GREP \ EGREP \ FGREP \ LD \ NM \ LN_S \ lt_SP2NL \ lt_NL2SP \ reload_flag \ OBJDUMP \ deplibs_check_method \ file_magic_cmd \ file_magic_glob \ want_nocaseglob \ DLLTOOL \ sharedlib_from_linklib_cmd \ AR \ archiver_list_spec \ STRIP \ RANLIB \ CC \ CFLAGS \ compiler \ lt_cv_sys_global_symbol_pipe \ lt_cv_sys_global_symbol_to_cdecl \ lt_cv_sys_global_symbol_to_import \ lt_cv_sys_global_symbol_to_c_name_address \ lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ lt_cv_nm_interface \ nm_file_list_spec \ lt_cv_truncate_bin \ lt_prog_compiler_no_builtin_flag \ lt_prog_compiler_pic \ lt_prog_compiler_wl \ lt_prog_compiler_static \ lt_cv_prog_compiler_c_o \ need_locks \ MANIFEST_TOOL \ DSYMUTIL \ NMEDIT \ LIPO \ OTOOL \ OTOOL64 \ shrext_cmds \ export_dynamic_flag_spec \ whole_archive_flag_spec \ compiler_needs_object \ with_gnu_ld \ allow_undefined_flag \ no_undefined_flag \ hardcode_libdir_flag_spec \ hardcode_libdir_separator \ exclude_expsyms \ include_expsyms \ file_list_spec \ variables_saved_for_relink \ libname_spec \ library_names_spec \ soname_spec \ install_override_mode \ finish_eval \ old_striplib \ striplib; do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in reload_cmds \ old_postinstall_cmds \ old_postuninstall_cmds \ old_archive_cmds \ extract_expsyms_cmds \ old_archive_from_new_cmds \ old_archive_from_expsyms_cmds \ archive_cmds \ archive_expsym_cmds \ module_cmds \ module_expsym_cmds \ export_symbols_cmds \ prelink_cmds \ postlink_cmds \ postinstall_cmds \ postuninstall_cmds \ finish_cmds \ sys_lib_search_path_spec \ configure_time_dlsearch_path \ configure_time_lt_sys_library_path; do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done ac_aux_dir='$ac_aux_dir' # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi PACKAGE='$PACKAGE' VERSION='$VERSION' RM='$RM' ofile='$ofile' _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "doc/Doxyfile") CONFIG_FILES="$CONFIG_FILES doc/Doxyfile" ;; "wimlib.pc") CONFIG_FILES="$CONFIG_FILES wimlib.pc" ;; "programs/mkwinpeimg") CONFIG_FILES="$CONFIG_FILES programs/mkwinpeimg" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac ac_MKDIR_P=$MKDIR_P case $MKDIR_P in [\\/$]* | ?:[\\/]* ) ;; */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t s&@MKDIR_P@&$ac_MKDIR_P&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi # Compute "$ac_file"'s index in $config_headers. _am_arg="$ac_file" _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || $as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$_am_arg" : 'X\(//\)[^/]' \| \ X"$_am_arg" : 'X\(//\)$' \| \ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$_am_arg" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'`/stamp-h$_am_stamp_count ;; :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 $as_echo "$as_me: executing $ac_file commands" >&6;} ;; esac case $ac_file$ac_mode in "depfiles":C) test x"$AMDEP_TRUE" != x"" || { # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. # TODO: see whether this extra hack can be removed once we start # requiring Autoconf 2.70 or later. case $CONFIG_FILES in #( *\'*) : eval set x "$CONFIG_FILES" ;; #( *) : set x $CONFIG_FILES ;; #( *) : ;; esac shift # Used to flag and report bootstrapping failures. am_rc=0 for am_mf do # Strip MF so we end up with the name of the file. am_mf=`$as_echo "$am_mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile which includes # dependency-tracking related rules and includes. # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ || continue am_dirpart=`$as_dirname -- "$am_mf" || $as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$am_mf" : 'X\(//\)[^/]' \| \ X"$am_mf" : 'X\(//\)$' \| \ X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$am_mf" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` am_filepart=`$as_basename -- "$am_mf" || $as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \ X"$am_mf" : 'X\(//\)$' \| \ X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$am_mf" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` { echo "$as_me:$LINENO: cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles" >&5 (cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } || am_rc=$? done if test $am_rc -ne 0; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "Something went wrong bootstrapping makefile fragments for automatic dependency tracking. Try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking). See \`config.log' for more details" "$LINENO" 5; } fi { am_dirpart=; unset am_dirpart;} { am_filepart=; unset am_filepart;} { am_mf=; unset am_mf;} { am_rc=; unset am_rc;} rm -f conftest-deps.mk } ;; "libtool":C) # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi cfgfile=${ofile}T trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # Generated automatically by $as_me ($PACKAGE) $VERSION # Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: # NOTE: Changes made to this file will be lost: look at ltmain.sh. # Provide generalized library-building support services. # Written by Gordon Matzigkeit, 1996 # Copyright (C) 2014 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program or library that is built # using GNU Libtool, you may include this file under the same # distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # The names of the tagged configurations supported by this script. available_tags='' # Configured defaults for sys_lib_dlsearch_path munging. : \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} # ### BEGIN LIBTOOL CONFIG # Which release of libtool.m4 was used? macro_version=$macro_version macro_revision=$macro_revision # Whether or not to build shared libraries. build_libtool_libs=$enable_shared # Whether or not to build static libraries. build_old_libs=$enable_static # What type of objects to build. pic_mode=$pic_mode # Whether or not to optimize for fast installation. fast_install=$enable_fast_install # Shared archive member basename,for filename based shared library versioning on AIX. shared_archive_member_spec=$shared_archive_member_spec # Shell to use when invoking shell scripts. SHELL=$lt_SHELL # An echo program that protects backslashes. ECHO=$lt_ECHO # The PATH separator for the build system. PATH_SEPARATOR=$lt_PATH_SEPARATOR # The host system. host_alias=$host_alias host=$host host_os=$host_os # The build system. build_alias=$build_alias build=$build build_os=$build_os # A sed program that does not truncate output. SED=$lt_SED # Sed that helps us avoid accidentally triggering echo(1) options like -n. Xsed="\$SED -e 1s/^X//" # A grep program that handles long lines. GREP=$lt_GREP # An ERE matcher. EGREP=$lt_EGREP # A literal string matcher. FGREP=$lt_FGREP # A BSD- or MS-compatible name lister. NM=$lt_NM # Whether we need soft or hard links. LN_S=$lt_LN_S # What is the maximum length of a command? max_cmd_len=$max_cmd_len # Object file suffix (normally "o"). objext=$ac_objext # Executable file suffix (normally ""). exeext=$exeext # whether the shell understands "unset". lt_unset=$lt_unset # turn spaces into newlines. SP2NL=$lt_lt_SP2NL # turn newlines into spaces. NL2SP=$lt_lt_NL2SP # convert \$build file names to \$host format. to_host_file_cmd=$lt_cv_to_host_file_cmd # convert \$build files to toolchain format. to_tool_file_cmd=$lt_cv_to_tool_file_cmd # An object symbol dumper. OBJDUMP=$lt_OBJDUMP # Method to check whether dependent libraries are shared objects. deplibs_check_method=$lt_deplibs_check_method # Command to use when deplibs_check_method = "file_magic". file_magic_cmd=$lt_file_magic_cmd # How to find potential files when deplibs_check_method = "file_magic". file_magic_glob=$lt_file_magic_glob # Find potential files using nocaseglob when deplibs_check_method = "file_magic". want_nocaseglob=$lt_want_nocaseglob # DLL creation program. DLLTOOL=$lt_DLLTOOL # Command to associate shared and link libraries. sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd # The archiver. AR=$lt_AR # Flags to create an archive (by configure). lt_ar_flags=$lt_ar_flags # Flags to create an archive. AR_FLAGS=\${ARFLAGS-"\$lt_ar_flags"} # How to feed a file listing to the archiver. archiver_list_spec=$lt_archiver_list_spec # A symbol stripping program. STRIP=$lt_STRIP # Commands used to install an old-style archive. RANLIB=$lt_RANLIB old_postinstall_cmds=$lt_old_postinstall_cmds old_postuninstall_cmds=$lt_old_postuninstall_cmds # Whether to use a lock for old archive extraction. lock_old_archive_extraction=$lock_old_archive_extraction # A C compiler. LTCC=$lt_CC # LTCC compiler flags. LTCFLAGS=$lt_CFLAGS # Take the output of nm and produce a listing of raw symbols and C names. global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe # Transform the output of nm in a proper C declaration. global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl # Transform the output of nm into a list of symbols to manually relocate. global_symbol_to_import=$lt_lt_cv_sys_global_symbol_to_import # Transform the output of nm in a C name address pair. global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address # Transform the output of nm in a C name address pair when lib prefix is needed. global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix # The name lister interface. nm_interface=$lt_lt_cv_nm_interface # Specify filename containing input files for \$NM. nm_file_list_spec=$lt_nm_file_list_spec # The root where to search for dependent libraries,and where our libraries should be installed. lt_sysroot=$lt_sysroot # Command to truncate a binary pipe. lt_truncate_bin=$lt_lt_cv_truncate_bin # The name of the directory that contains temporary libtool files. objdir=$objdir # Used to examine libraries when file_magic_cmd begins with "file". MAGIC_CMD=$MAGIC_CMD # Must we lock files when doing compilation? need_locks=$lt_need_locks # Manifest tool. MANIFEST_TOOL=$lt_MANIFEST_TOOL # Tool to manipulate archived DWARF debug symbol files on Mac OS X. DSYMUTIL=$lt_DSYMUTIL # Tool to change global to local symbols on Mac OS X. NMEDIT=$lt_NMEDIT # Tool to manipulate fat objects and archives on Mac OS X. LIPO=$lt_LIPO # ldd/readelf like tool for Mach-O binaries on Mac OS X. OTOOL=$lt_OTOOL # ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. OTOOL64=$lt_OTOOL64 # Old archive suffix (normally "a"). libext=$libext # Shared library suffix (normally ".so"). shrext_cmds=$lt_shrext_cmds # The commands to extract the exported symbol list from a shared archive. extract_expsyms_cmds=$lt_extract_expsyms_cmds # Variables whose values should be saved in libtool wrapper scripts and # restored at link time. variables_saved_for_relink=$lt_variables_saved_for_relink # Do we need the "lib" prefix for modules? need_lib_prefix=$need_lib_prefix # Do we need a version for libraries? need_version=$need_version # Library versioning type. version_type=$version_type # Shared library runtime path variable. runpath_var=$runpath_var # Shared library path variable. shlibpath_var=$shlibpath_var # Is shlibpath searched before the hard-coded library search path? shlibpath_overrides_runpath=$shlibpath_overrides_runpath # Format of library name prefix. libname_spec=$lt_libname_spec # List of archive names. First name is the real one, the rest are links. # The last name is the one that the linker finds with -lNAME library_names_spec=$lt_library_names_spec # The coded name of the library, if different from the real name. soname_spec=$lt_soname_spec # Permission mode override for installation of shared libraries. install_override_mode=$lt_install_override_mode # Command to use after installation of a shared archive. postinstall_cmds=$lt_postinstall_cmds # Command to use after uninstallation of a shared archive. postuninstall_cmds=$lt_postuninstall_cmds # Commands used to finish a libtool library installation in a directory. finish_cmds=$lt_finish_cmds # As "finish_cmds", except a single script fragment to be evaled but # not shown. finish_eval=$lt_finish_eval # Whether we should hardcode library paths into libraries. hardcode_into_libs=$hardcode_into_libs # Compile-time system search path for libraries. sys_lib_search_path_spec=$lt_sys_lib_search_path_spec # Detected run-time system search path for libraries. sys_lib_dlsearch_path_spec=$lt_configure_time_dlsearch_path # Explicit LT_SYS_LIBRARY_PATH set during ./configure time. configure_time_lt_sys_library_path=$lt_configure_time_lt_sys_library_path # Whether dlopen is supported. dlopen_support=$enable_dlopen # Whether dlopen of programs is supported. dlopen_self=$enable_dlopen_self # Whether dlopen of statically linked programs is supported. dlopen_self_static=$enable_dlopen_self_static # Commands to strip libraries. old_striplib=$lt_old_striplib striplib=$lt_striplib # The linker used to build libraries. LD=$lt_LD # How to create reloadable object files. reload_flag=$lt_reload_flag reload_cmds=$lt_reload_cmds # Commands used to build an old-style archive. old_archive_cmds=$lt_old_archive_cmds # A language specific compiler. CC=$lt_compiler # Is the compiler the GNU compiler? with_gcc=$GCC # Compiler flag to turn off builtin functions. no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag # Additional compiler flags for building library objects. pic_flag=$lt_lt_prog_compiler_pic # How to pass a linker flag through the compiler. wl=$lt_lt_prog_compiler_wl # Compiler flag to prevent dynamic linking. link_static_flag=$lt_lt_prog_compiler_static # Does compiler simultaneously support -c and -o options? compiler_c_o=$lt_lt_cv_prog_compiler_c_o # Whether or not to add -lc for building shared libraries. build_libtool_need_lc=$archive_cmds_need_lc # Whether or not to disallow shared libs when runtime libs are static. allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes # Compiler flag to allow reflexive dlopens. export_dynamic_flag_spec=$lt_export_dynamic_flag_spec # Compiler flag to generate shared objects directly from archives. whole_archive_flag_spec=$lt_whole_archive_flag_spec # Whether the compiler copes with passing no objects directly. compiler_needs_object=$lt_compiler_needs_object # Create an old-style archive from a shared archive. old_archive_from_new_cmds=$lt_old_archive_from_new_cmds # Create a temporary old-style archive to link instead of a shared archive. old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds # Commands used to build a shared archive. archive_cmds=$lt_archive_cmds archive_expsym_cmds=$lt_archive_expsym_cmds # Commands used to build a loadable module if different from building # a shared archive. module_cmds=$lt_module_cmds module_expsym_cmds=$lt_module_expsym_cmds # Whether we are building with GNU ld or not. with_gnu_ld=$lt_with_gnu_ld # Flag that allows shared libraries with undefined symbols to be built. allow_undefined_flag=$lt_allow_undefined_flag # Flag that enforces no undefined symbols. no_undefined_flag=$lt_no_undefined_flag # Flag to hardcode \$libdir into a binary during linking. # This must work even if \$libdir does not exist hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec # Whether we need a single "-rpath" flag with a separated argument. hardcode_libdir_separator=$lt_hardcode_libdir_separator # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary. hardcode_direct=$hardcode_direct # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary and the resulting library dependency is # "absolute",i.e impossible to change by setting \$shlibpath_var if the # library is relocated. hardcode_direct_absolute=$hardcode_direct_absolute # Set to "yes" if using the -LDIR flag during linking hardcodes DIR # into the resulting binary. hardcode_minus_L=$hardcode_minus_L # Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR # into the resulting binary. hardcode_shlibpath_var=$hardcode_shlibpath_var # Set to "yes" if building a shared library automatically hardcodes DIR # into the library and all subsequent libraries and executables linked # against it. hardcode_automatic=$hardcode_automatic # Set to yes if linker adds runtime paths of dependent libraries # to runtime path list. inherit_rpath=$inherit_rpath # Whether libtool must link a program against all its dependency libraries. link_all_deplibs=$link_all_deplibs # Set to "yes" if exported symbols are required. always_export_symbols=$always_export_symbols # The commands to list exported symbols. export_symbols_cmds=$lt_export_symbols_cmds # Symbols that should not be listed in the preloaded symbols. exclude_expsyms=$lt_exclude_expsyms # Symbols that must always be exported. include_expsyms=$lt_include_expsyms # Commands necessary for linking programs (against libraries) with templates. prelink_cmds=$lt_prelink_cmds # Commands necessary for finishing linking programs. postlink_cmds=$lt_postlink_cmds # Specify filename containing input files. file_list_spec=$lt_file_list_spec # How to hardcode a shared library path into an executable. hardcode_action=$hardcode_action # ### END LIBTOOL CONFIG _LT_EOF cat <<'_LT_EOF' >> "$cfgfile" # ### BEGIN FUNCTIONS SHARED WITH CONFIGURE # func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x$2 in x) ;; *:) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" ;; x:*) eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" ;; *::*) eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" ;; *) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" ;; esac } # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in $*""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } # ### END FUNCTIONS SHARED WITH CONFIGURE _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac ltmain=$ac_aux_dir/ltmain.sh # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? sed '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" ;; "programs/mkwinpeimg":F) chmod +x programs/mkwinpeimg ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi wimlib-1.13.1/configure.ac0000644000175000017500000002415713464166215012310 00000000000000############################################################################### AC_INIT([wimlib], [1.13.1], [ebiggers3@gmail.com]) AC_CONFIG_SRCDIR([src/wim.c]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([build-aux]) AM_INIT_AUTOMAKE([-Wall -Werror subdir-objects foreign]) AM_SILENT_RULES([yes]) AC_C_BIGENDIAN m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) LT_INIT AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([Makefile] [doc/Doxyfile] [wimlib.pc]) AC_CONFIG_FILES([programs/mkwinpeimg], [chmod +x programs/mkwinpeimg]) PKGCONFIG_PRIVATE_REQUIRES="" PKGCONFIG_PRIVATE_LIBS="" ############################################################################### # General platform features # ############################################################################### AC_PROG_CC AM_PROG_CC_C_O AC_CANONICAL_HOST WINDOWS_NATIVE_BUILD="no" PLATFORM_CPPFLAGS="" PLATFORM_CFLAGS="-fvisibility=hidden" PLATFORM_LDFLAGS="" case "$host_os" in mingw*) # Native Windows WINDOWS_NATIVE_BUILD="yes" # -D__MINGW_USE_VC2005_COMPAT: make time_t 64-bit on 32-bit Windows. PLATFORM_CPPFLAGS="-D_POSIX -D_POSIX_THREAD_SAFE_FUNCTIONS -DUNICODE -D_UNICODE -D_CRT_NON_CONFORMING_SWPRINTFS -D__MINGW_USE_VC2005_COMPAT" PLATFORM_CFLAGS="-municode -mno-ms-bitfields" PLATFORM_LDFLAGS="-no-undefined" WITH_NTFS_3G_DEFAULT="no" WITH_FUSE_DEFAULT="no" ;; linux*) # Linux WITH_NTFS_3G_DEFAULT="yes" WITH_FUSE_DEFAULT="yes" ;; *) # Other UNIX WITH_NTFS_3G_DEFAULT="yes" WITH_FUSE_DEFAULT="no" ;; esac AC_SUBST([PLATFORM_CPPFLAGS], [$PLATFORM_CPPFLAGS]) AC_SUBST([PLATFORM_CFLAGS], [$PLATFORM_CFLAGS]) AC_SUBST([PLATFORM_LDFLAGS], [$PLATFORM_LDFLAGS]) AM_CONDITIONAL([WINDOWS_NATIVE_BUILD], [test "$WINDOWS_NATIVE_BUILD" = "yes"]) # Useful functions which we can do without. AC_CHECK_FUNCS([futimens utimensat flock mempcpy \ openat fstatat readlinkat fdopendir posix_fallocate \ llistxattr lgetxattr fsetxattr lsetxattr getopt_long_only]) # Header checks, most of which are only here to satisfy conditional includes # made by the libntfs-3g headers. AC_CHECK_HEADERS([alloca.h \ byteswap.h \ endian.h \ errno.h \ glob.h \ machine/endian.h \ stdarg.h \ stddef.h \ stdlib.h \ sys/byteorder.h \ sys/endian.h \ sys/file.h \ sys/syscall.h \ sys/sysctl.h \ sys/times.h \ sys/xattr.h \ time.h \ utime.h]) # Does stat() support nanosecond-precision timestamps? (This is relevant on # UNIX but not on Windows.) AC_CHECK_MEMBER([struct stat.st_mtim], [AC_DEFINE([HAVE_STAT_NANOSECOND_PRECISION], [1], [Define to 1 if stat() supports nanosecond precision timestamps])], [], [#include ]) # Check for possible support for the Linux getrandom() system call AC_CHECK_DECL([__NR_getrandom], [AC_DEFINE([HAVE_NR_GETRANDOM], [1], [Define to 1 if the system headers define a system call number for getrandom()])], [], [#include ]) ############################################################################### # Required libraries # ############################################################################### # ------------------------------ pthreads ------------------------------------- AX_PTHREAD([], [AC_MSG_ERROR(["cannot find pthreads library"])]) # ------------------------------ libxml2 -------------------------------------- PKG_CHECK_MODULES([LIBXML2], [libxml-2.0]) PKGCONFIG_PRIVATE_REQUIRES="$PKGCONFIG_PRIVATE_REQUIRES libxml-2.0" ############################################################################### # Configuration options # ############################################################################### # ------------------------- ntfs-3g support ----------------------------------- AC_MSG_CHECKING([whether to include support for ntfs-3g]) AC_ARG_WITH([ntfs-3g], [AS_HELP_STRING([--without-ntfs-3g], [build without libntfs-3g. This will disable the ability to capture or apply a WIM image directly from/to an unmounted NTFS volume.])], [WITH_NTFS_3G=$withval], [WITH_NTFS_3G=$WITH_NTFS_3G_DEFAULT]) AC_MSG_RESULT([$WITH_NTFS_3G]) if test "$WITH_NTFS_3G" = "yes"; then PKG_CHECK_MODULES([LIBNTFS_3G], [libntfs-3g >= 2011.4.12], [], [AC_MSG_ERROR([Cannot find libntfs-3g version 2011-4-12 or later! Without libntfs-3g, wimlib cannot include support for capturing or applying a WIM image directly from/to an unmounted NTFS volume while preserving NTFS-specific data such as security descriptors and named data streams. Either install libntfs-3g, or configure --without-ntfs-3g to disable this feature.])]) PKGCONFIG_PRIVATE_REQUIRES="$PKGCONFIG_PRIVATE_REQUIRES libntfs-3g" AC_DEFINE([WITH_NTFS_3G], [1], [Define to 1 if using NTFS-3G support]) fi AM_CONDITIONAL([WITH_NTFS_3G], [test "$WITH_NTFS_3G" = "yes"]) # ------------------------ FUSE mount support --------------------------------- AC_MSG_CHECKING([whether to include support for mounting WIMs]) AC_ARG_WITH([fuse], [AS_HELP_STRING([--without-fuse], [build without libfuse. This will disable the ability to mount WIM images.])], [WITH_FUSE=$withval], [WITH_FUSE=$WITH_FUSE_DEFAULT]) AC_MSG_RESULT([$WITH_FUSE]) if test "$WITH_FUSE" = "yes"; then PKG_CHECK_MODULES([LIBFUSE], [fuse], [], [AC_MSG_ERROR([Cannot find libfuse! Without libfuse, wimlib cannot include support for mounting WIM images. Either install libfuse, or configure --without-fuse to disable this feature.])]) PKGCONFIG_PRIVATE_REQUIRES="$PKGCONFIG_PRIVATE_REQUIRES fuse" AC_DEFINE([WITH_FUSE], [1], [Define to 1 if using FUSE support]) AC_CHECK_LIB([rt], [mq_open], [], [AC_MSG_ERROR([Cannot find librt (the POSIX.1b Realtime Extensions Library)! wimlib needs this for the POSIX message queue functions, which are used in the code for mounting WIM images. Recent versions of glibc include this library. Either install this library, or configure --without-fuse to disable support for mounting WIM images.])]) PKGCONFIG_PRIVATE_LIBS="$PKGCONFIG_PRIVATE_LIBS -lrt" AC_SUBST([LIBRT_LIBS], [-lrt]) fi AM_CONDITIONAL([WITH_FUSE], [test "$WITH_FUSE" = "yes"]) # ------------------------ SHA-1 implementation --------------------------------- AC_MSG_CHECKING([whether to use SSSE3-accelerated SHA-1]) AC_ARG_ENABLE([ssse3-sha1], [AS_HELP_STRING([--enable-ssse3-sha1], [Include SSSE3-accelerated SHA-1 implementation by Intel. This implies --without-libcrypto.])], [ENABLE_SSSE3_SHA1=$enableval], [ENABLE_SSSE3_SHA1=no]) AC_MSG_RESULT([$ENABLE_SSSE3_SHA1]) if test "$ENABLE_SSSE3_SHA1" = "yes" ; then AC_DEFINE([ENABLE_SSSE3_SHA1], [1], [Define to 1 if using SSSE3 implementation of SHA-1]) AC_PROG_NASM NASM_SYMBOL_PREFIX="" NASM_PLATFORM_FLAGS="" if test "$WINDOWS_NATIVE_BUILD" = "yes"; then NASM_PLATFORM_FLAGS="-DWIN_ABI" fi case "$host_os" in darwin* | rhapsody* | nextstep* | openstep* | macos*) NASM_SYMBOL_PREFIX="_" ;; esac AC_SUBST([NASM_PLATFORM_FLAGS], [$NASM_PLATFORM_FLAGS]) AC_SUBST([NASM_SYMBOL_PREFIX], [$NASM_SYMBOL_PREFIX]) else AC_MSG_CHECKING([whether to use SHA-1 implementation from system libcrypto]) AC_ARG_WITH([libcrypto], [AS_HELP_STRING([--without-libcrypto], [build in the SHA-1 algorithm, rather than use external libcrypto from OpenSSL (default is autodetect)])], [WITH_LIBCRYPTO=$withval], [WITH_LIBCRYPTO=auto]) AC_MSG_RESULT([$WITH_LIBCRYPTO]) if test "$WITH_LIBCRYPTO" != "no"; then PKG_CHECK_MODULES([LIBCRYPTO], [libcrypto], [ PKGCONFIG_PRIVATE_REQUIRES="$PKGCONFIG_PRIVATE_REQUIRES libcrypto" AC_DEFINE([WITH_LIBCRYPTO], [1], [Define to 1 if using libcrypto SHA-1]) ], [AC_MSG_WARN([Cannot find libcrypto: using stand-alone SHA-1 code instead])]) fi fi AM_CONDITIONAL([ENABLE_SSSE3_SHA1], [test "$ENABLE_SSSE3_SHA1" = "yes"]) # ----------------------------- Other options --------------------------------- AC_MSG_CHECKING([whether to include error messages]) AC_ARG_ENABLE([error_messages], AS_HELP_STRING([--disable-error-messages], [do not compile in error messsages]), [ENABLE_ERROR_MESSAGES=$enableval], [ENABLE_ERROR_MESSAGES=yes]) AC_MSG_RESULT([$ENABLE_ERROR_MESSAGES]) if test "$ENABLE_ERROR_MESSAGES" = "yes"; then AC_DEFINE([ENABLE_ERROR_MESSAGES], [1], [Define to 1 if including error messages]) fi AC_MSG_CHECKING([whether to include assertions]) AC_ARG_ENABLE([assertions], AS_HELP_STRING([--disable-assertions], [do not include assertions]), [ENABLE_ASSERTIONS=$enableval], [ENABLE_ASSERTIONS=yes]) AC_MSG_RESULT([$ENABLE_ASSERTIONS]) if test "$ENABLE_ASSERTIONS" = "yes"; then AC_DEFINE([ENABLE_ASSERTIONS], [1], [Define to 1 if including assertions]) fi AC_MSG_CHECKING([whether to include support for multi-threaded compression]) AC_ARG_ENABLE([multithreaded-compression], AS_HELP_STRING([--disable-multithreaded-compression], [disable support for multithreaded compression]), [ENABLE_MULTITHREADED_COMPRESSION=$enableval], [ENABLE_MULTITHREADED_COMPRESSION=yes]) AC_MSG_RESULT([$ENABLE_MULTITHREADED_COMPRESSION]) if test "$ENABLE_MULTITHREADED_COMPRESSION" = "yes"; then AC_DEFINE([ENABLE_MULTITHREADED_COMPRESSION], [1], [Define to 1 to support multithreaded compression]) fi AC_ARG_WITH(pkgconfigdir, [ --with-pkgconfigdir=DIR pkgconfig file in DIR @<:@LIBDIR/pkgconfig@:>@], [pkgconfigdir=$withval], [pkgconfigdir='${libdir}/pkgconfig']) AC_SUBST(pkgconfigdir) AC_MSG_CHECKING([whether to enable supporting code for tests]) AC_ARG_ENABLE([test-support], [AS_HELP_STRING([--enable-test-support], [Enable supporting code for tests (developers only)])], [ENABLE_TEST_SUPPORT=$enableval], [ENABLE_TEST_SUPPORT=no]) AC_MSG_RESULT([$ENABLE_TEST_SUPPORT]) if test "$ENABLE_TEST_SUPPORT" = "yes" ; then AC_DEFINE([ENABLE_TEST_SUPPORT], [1], [Define to 1 to enable supporting code for tests]) fi AM_CONDITIONAL([ENABLE_TEST_SUPPORT], [test "$ENABLE_TEST_SUPPORT" = "yes"]) ############################################################################### AC_SUBST([PKGCONFIG_PRIVATE_REQUIRES], [$PKGCONFIG_PRIVATE_REQUIRES]) AC_SUBST([PKGCONFIG_PRIVATE_LIBS], [$PKGCONFIG_PRIVATE_LIBS]) AC_OUTPUT wimlib-1.13.1/README0000644000175000017500000003371613464166215010703 00000000000000 INTRODUCTION This is wimlib version 1.13.1 (May 2019). wimlib is a C library for creating, modifying, extracting, and mounting files in the Windows Imaging Format (WIM files). wimlib and its command-line frontend 'wimlib-imagex' provide a free and cross-platform alternative to Microsoft's WIMGAPI, ImageX, and DISM. INSTALLATION To install wimlib and wimlib-imagex on UNIX-like systems, you can compile from source (e.g. './configure && make && sudo make install'). Alternatively, check if a package has already been prepared for your operating system. Example files for Debian and RPM packaging are in the debian/ and rpm/ directories. To install wimlib and wimlib-imagex on Windows, just download and extract the ZIP file containing the latest binaries. See README.WINDOWS for more details. All official wimlib releases are available from https://wimlib.net. WIM FILES A Windows Imaging (WIM) file is an archive designed primarily for archiving Windows filesystems. However, it can be used on other platforms as well, with some limitations. Like some other archive formats such as ZIP, files in WIM archives may be compressed. WIM archives support multiple compression formats, including LZX, XPRESS, and LZMS. All these formats are supported by wimlib. A WIM archive contains one or more "images", each of which is a logically independent directory tree. Each image has a 1-based index and usually a name. WIM archives provide data deduplication at the level of full file contents. In other words, each unique "file contents" is only stored once in the archive, regardless of how many files have that contents across all images. A WIM archive may be either stand-alone or split into multiple parts. An update of the WIM format --- first added by Microsoft for Windows 8 --- supports solid-mode compression. This refers to files being compressed together (e.g. as in a .tar.xz or .7z archive) rather than separately (e.g. as in a .zip archive). This usually produces a much better compression ratio. Solid archives are sometimes called "ESD files" by Microsoft and may have the ".esd" file extension rather than ".wim". They are supported in wimlib since v1.6.0. IMAGEX IMPLEMENTATION wimlib itself is a C library, and it provides a documented public API (See: https://wimlib.net/apidoc) for other programs to use. However, it is also distributed with a command-line program called "wimlib-imagex" that uses this library to implement an imaging tool similar to Microsoft's ImageX. wimlib-imagex supports almost all the capabilities of Microsoft's ImageX as well as additional capabilities. wimlib-imagex works on both UNIX-like systems and Windows, although some features differ between the platforms. Run `wimlib-imagex' with no arguments to see an overview of the available commands and their syntax. Note that the commands have both long and short forms, e.g. `wimlib-imagex apply' is equivalent to `wimapply'. For additional documentation: * If you have installed wimlib-imagex on a UNIX-like system, you will find further documentation in the man pages; run `man wimlib-imagex' to get started. * If you have downloaded the Windows binary distribution, you will find the documentation for wimlib-imagex in PDF format in the "doc" directory. Note that although the documentation is written in the style of UNIX manual pages, it does document Windows-specific behavior when relevant. COMPRESSION wimlib (and wimlib-imagex) can create XPRESS, LZX, and LZMS compressed WIM archives. wimlib's compression codecs usually outperform and outcompress their closed-source Microsoft equivalents. Multiple compression levels and chunk sizes as well as solid mode compression are supported. Compression is multithreaded by default. Detailed benchmark results and descriptions of the algorithms used can be found at https://wimlib.net/compression.html. NTFS SUPPORT WIM images may contain data, such as named data streams and compression/encryption flags, that are best represented on the NTFS filesystem used on Windows. Also, WIM images may contain security descriptors which are specific to Windows and cannot be represented on other operating systems. wimlib handles this NTFS-specific or Windows-specific data in a platform-dependent way: * In the Windows version of wimlib and wimlib-imagex, NTFS-specific and Windows-specific data are supported natively. * In the UNIX version of wimlib and wimlib-imagex, NTFS-specific and Windows-specific data are ordinarily ignored; however, there is also special support for capturing and extracting images directly to/from unmounted NTFS volumes. This was made possible with the help of libntfs-3g from the NTFS-3G project. For both platforms the code for NTFS capture and extraction is complete enough that it is possible to apply an image from the "install.wim" contained in recent Windows installation media (Vista or later) directly to an NTFS filesystem, and then boot Windows from it after preparing the Boot Configuration Data. In addition, a Windows installation can be captured (or backed up) into a WIM file, and then re-applied later. WINDOWS PE wimlib can also be used to create customized images of Windows PE on either UNIX-like systems or Windows. Windows PE (Preinstallation Environment) is a lightweight version of Windows that runs entirely from memory and can be used to perform maintenance or to install Windows. It is the operating system that runs when you boot from the Windows installation media. A copy of Windows PE can be found on the installation media for Windows (Vista or later) as the file `sources/boot.wim', or in the Windows Automated Installation Kit (WAIK), which is free to download from Microsoft. A shell script `mkwinpeimg' is provided with wimlib on UNIX-like systems to simplify the process of creating and customizing a bootable Windows PE image, sourcing the needed files from the Windows installation media or from the WAIK. DEPENDENCIES This section documents the dependencies of wimlib and the programs distributed with it, when building for a UNIX-like system from source. If you have downloaded the Windows binary distribution of wimlib and wimlib-imagex then all dependencies were already included and this section is irrelevant. * libxml2 (required) This is a commonly used free library to read and write XML documents. Almost all Linux distributions should include this; however, you may need to install the header files, which might be in a package named "libxml2-dev" or similar. For more information see http://xmlsoft.org/. * libfuse (optional but recommended) Unless configured --without-fuse, wimlib requires a non-ancient version of libfuse. Most Linux distributions already include this, but make sure you have the libfuse package installed, and also libfuse-dev if your distribution distributes header files separately. FUSE also requires a kernel module. If the kernel module is available it should automatically be loaded if you try to mount a WIM image. For more information see http://fuse.sourceforge.net/. * libntfs-3g (optional but recommended) Unless configured --without-ntfs-3g, wimlib requires the library and headers for libntfs-3g to be installed. The minimum required version is 2011-4-12, but newer versions contain important bug fixes. * OpenSSL / libcrypto (optional) wimlib can use the SHA-1 message digest implementation from libcrypto (usually provided by OpenSSL) instead of compiling in yet another SHA-1 implementation. * cdrkit (optional) * mtools (optional) * syslinux (optional) * cabextract (optional) The `mkwinpeimg' shell script will look for several other programs depending on what options are given to it. Depending on your Linux distribution, you may already have these programs installed, or they may be in the software repository. Making an ISO filesystem requires `mkisofs' from `cdrkit' (http://www.cdrkit.org). Making a disk image requires `mtools' (http://www.gnu.org/software/mtools) and `syslinux' (http://www.syslinux.org). Retrieving files from the Windows Automated Installation Kit requires `cabextract' (http://www.cabextract.org.uk). CONFIGURATION This section documents the most important options that may be passed to the "configure" script when building from source: --without-ntfs-3g If libntfs-3g is not available or is not version 2011-4-12 or later, wimlib can be built without it, in which case it will not be possible to capture or apply WIM images directly from/to NTFS volumes. The default is --with-ntfs-3g when building for any UNIX-like system, and --without-ntfs-3g when building for Windows. --without-fuse The --without-fuse option disables support for mounting WIM images. This removes dependencies on libfuse and librt. The wimmount, wimmountrw, and wimunmount commands will not work. The default is --with-fuse when building for Linux, and --without-fuse otherwise. --without-libcrypto Build in functions for SHA-1 rather than using external SHA-1 functions from libcrypto (usually provided by OpenSSL). The default is to use libcrypto if it is found on your system. PORTABILITY wimlib works on both UNIX-like systems (Linux, Mac OS X, FreeBSD, etc.) and Windows (XP and later). As much code as possible is shared among all supported platforms, but there necessarily are some differences in what features are supported on each platform and how they are implemented. Most notable is that file tree scanning and extraction are implemented separately for Windows, UNIX, and UNIX (NTFS-3G mode), to ensure a fast and feature-rich implementation of each platform/mode. wimlib is mainly used on x86 and x86_64 CPUs, but it should also work on a number of other GCC-supported 32-bit or 64-bit architectures. It has been tested on the ARM and MIPS architectures. Currently, gcc and clang are the only supported compilers. A few nonstandard extensions are used in the code. REFERENCES The WIM file format is partially specified in a document that can be found in the Microsoft Download Center. However, this document really only provides an overview of the format and is not a formal specification. It also does not cover later extensions of the format, such as solid resources. With regards to the supported compression formats: - Microsoft has official documentation for XPRESS that is of reasonable quality. - Microsoft has official documentation for LZX, but in two different documents, neither of which is completely applicable to its use in the WIM format, and the first of which contains multiple errors. - There does not seem to be any official documentation for LZMS, so my comments and code in src/lzms_decompress.c may in fact be the best documentation available for this particular compression format. The algorithms used by wimlib's compression and decompression codecs are inspired by a variety of sources, including open source projects and computer science papers. The code in ntfs-3g_apply.c and ntfs-3g_capture.c uses the NTFS-3G library, which is a library for reading and writing to NTFS filesystems (the filesystem used by recent versions of Windows). See http://www.tuxera.com/community/open-source-ntfs-3g/ for more information. A limited number of other free programs can handle some parts of the WIM file format: * 7-Zip is able to extract and create WIMs (as well as files in many other archive formats). However, wimlib is designed specifically to handle WIM files and provides features previously only available in Microsoft's implementation, such as the ability to mount WIMs read-write as well as read-only, the ability to create compressed WIMs, the correct handling of security descriptors and hard links, support for LZMS compression, and support for solid archives. * ImagePyX (https://github.com/maxpat78/ImagePyX) is a Python program that provides some capabilities of wimlib-imagex, with the help of external compression codecs. If you are looking for an archive format that provides features similar to WIM but was designed primarily for UNIX, you may want to consider SquashFS (http://squashfs.sourceforge.net/). However, you may find that wimlib works surprisingly well on UNIX. It will store hard links and symbolic links, and it supports storing standard UNIX file permissions (owners, groups, and modes); special files such as device nodes and FIFOs; and extended attributes. Actually, I use it to back up my own files on Linux! HISTORY wimlib was originally a project started by Carl Thijssen for use on Linux in the Ultimate Deployment Appliance (http://www.ultimatedeployment.org/). Since then the code has been entirely rewritten and improved (main author: Eric Biggers). Windows support has been available since version 1.3.0 (March 2013). A list of version-to-version changes can be found in the NEWS file. NOTICES wimlib is free software that comes with NO WARRANTY, to the extent permitted by law. See the COPYING file for more details. Bug reports, suggestions, and other contributions are appreciated and may be sent via email to ebiggers3@gmail.com or posted to https://wimlib.net/forums. wimlib is independently developed and does not contain any code, data, or files copyrighted by Microsoft. It is not known to be affected by any patents. On UNIX-like systems, if you do not want wimlib to be dynamically linked with libcrypto (OpenSSL), configure with --without-libcrypto. This replaces the SHA1 implementation with built-in code and there will be no difference in functionality. Note: copyright years may be listed using range notation, e.g., 2012-2016, indicating that every year in the range, inclusive, is a copyrightable year that would otherwise be listed individually. wimlib-1.13.1/NEWS0000644000175000017500000012741013464166215010515 00000000000000Version 1.13.1: Fixed a crash or incorrect output during LZMS compression with a compression level greater than 50 and a chunk size greater than 64 MiB. This affected wimlib v1.8.0 and later. In the unlikely event that you used all these non-default compression settings in combination, e.g. 'wimcapture --solid --solid-compress=LZMS:100 --solid-chunk-size=128M', run 'wimverify' on your archives to verify your data is intact. Version 1.13.0: On Windows, wimlib now supports capturing and applying extended attributes (EAs). It is compatible with DISM with the /EA option, available since Windows 10 version 1607. wimlib's EA support is on by default and works on older versions of Windows too. Partially fixed a bug where [ExclusionException] entries didn't take effect when the containing directory is matched by [ExclusionList]. It now works when the [ExclusionException] patterns are absolute. For example, listing "/dir/file" in [ExclusionException] now works even if "/dir" is matched by [ExclusionList]. Added a '--create' option to 'wimappend' which makes it create the WIM file (like 'wimcapture') if it doesn't exist yet. Added an '--include-integrity' option to various wimlib-imagex commands. '--include-integrity' is like '--check', but it will just include an integrity table in the output WIM(s), while skipping verification of any existing integrity tables. This can be useful to avoid unwanted verification of large WIM files, e.g. WIMs given by '--delta-from'. 'wimextract' now reads a pathlist file from standard input when "@-" is given as an argument. wimsplit (API: wimlib_split()) now correctly handles a dot in the path to the first split WIM part, prior to the filename extension. 'wimlib-imagex --version' now shows the version of the library it is actually using (in case it is different from wimlib-imagex's version). Version 1.12.0: Fixed a bug that was causing the LZMS decompressor to be miscompiled with GCC 7 (this broke extracting "solid" archives). The Windows 10 Recycle Bin directory (\$RECYCLE.BIN) has been added to the default exclusion list. Added a '--quiet' option to wimlib-imagex. The 'mkwinpeimg' script now also looks for the syslinux BIOS modules in the directory /usr/lib/syslinux/modules/bios. Files with timestamps before the year 1970 are now extracted correctly to UNIX-style filesystems, are displayed correctly by 'wimdir --detailed', and show up correctly in mounted WIM images. Files with timestamps after the year 2038 are now displayed correctly by the 32-bit Windows build of wimlib. Version 1.11.0: Fixed a data corruption bug (incorrect compression) when storing an already highly-compressed file in an LZX-compressed WIM with a chunk size greater than or equal to 64K. Note that this is not the default setting and such WIMs are not supported by Microsoft's WIM software, so only users who used the --chunk-size option to wimlib-imagex or the wimlib_set_output_chunk_size() API function may have been affected. This bug was introduced in wimlib v1.10.0. See https://wimlib.net/forums/viewtopic.php?f=1&t=300 for more details. On all platforms, sparse files are now extracted as sparse. Sparse files captured from UNIX-style filesystems are now marked as sparse in the resulting WIM image. Added support for storing Linux-style extended attributes in WIM images. When the --unix-data option is used on Linux, wimlib-imagex now captures and applies extended attributes, in addition to the already-supported standard UNIX file permissions (owner/group/mode) and special files. --delta-from is now supported by wimappend. (Previously it was only supported by wimcapture.) On Windows, improved the way in which files deduplicated with Windows' Data Deduplication feature are captured. The development files needed to link with wimlib using Visual Studio are now included in the Windows release archives. wimlib.h can now be included by Visual Studio without errors. The example programs can now be compiled in C++ mode, and they also now work on Windows. Updated 'mkwinpeimg' to work correctly on images that have a "windows" (lower case) directory rather than a "Windows" (upper case) directory. Fixed configuring with --enable-ssse3-sha1 from release tarball (the file nasm_lt.sh was missing). Made some documentation improvements. Version 1.10.0: The LZX compression ratio has been slightly improved. The default mode, LZX level 50, is now almost as good as the old LZX level 100, while being nearly the same speed as before. Decompression performance has been slightly improved. Filenames are now always listed in NTFS collation order. On UNIX-like systems, wimlib can now process Windows filenames that are not valid Unicode due to the presence of unpaired surrogates. On UNIX-like systems, wimlib now always assumes UTF-8 encoding with the addition of surrogate codepoints. Consequently, the environmental variable WIMLIB_IMAGEX_USE_UTF8 and the flag WIMLIB_INIT_FLAG_ASSUME_UTF8 no longer have any effect. wimlib no longer depends on iconv. Reduced memory usage slightly. When a WIM image is applied in NTFS-3G mode, security descriptors are now created in NTFS v3.0 format when supported by the volume. Workarounds for bugs in libntfs-3g version 2013.1.13 and earlier have been removed. Users are advised to upgrade to a later version of libntfs-3g. On Windows, wimlib now supports case-sensitive filename extraction when supported by the underlying operating system and filesystem (operating system support requires a registry setting). Version 1.9.2: On UNIX, wimlib can now overwrite readonly files when extracting. On Windows, fixed a bug where wimlib could leave a null DACL (a.k.a. "no NTFS permissions") set on some existing directories after extraction. On Windows, when applying a WIM image in "WIMBoot mode" when the WOF driver is not loaded, wimlib can now correctly register a new WIM file with the target volume when the target volume previously had had WIM files unregistered. Added a new testing program. Clarified the main license text and updated public domain dedications for certain files to be more thorough. Version 1.9.1: Object IDs are now saved and restored on Windows and in NTFS-3G mode. Reduced memory usage when exporting large numbers of WIM images. Non UTF-8 locales are now detected correctly. Addressed compiler warnings and enabled "silent" make rules by default. Windows-specific updates: Fixed a bug where duplicate backslashes could be generated in link targets when extracting symbolic links and junctions. Fixed a bug where the .cmd shortcuts for wimlib-imagex wouldn't work if their full path contained a space. Fixed bugs related to scanning SMB filesystems. Added warning message about known issue with WindowsApps folder. Added instructions for building from source on Windows. VSS support is no longer marked "experimental". Added missing license file for libdivsufsort-lite. Version 1.9.0: Added experimental support for Windows VSS (Volume Shadow Copy Service). The new '--snapshot' argument to 'wimcapture' makes wimlib automatically create and use a temporary VSS snapshot when capturing a WIM image. Implemented setting of Windows-specific XML information, such as architecture, system root, and version details. This information is now automatically set in newly captured WIM images, when appropriate. Improved performance of directory tree scans on Windows. On Windows, to improve capture performance, wimlib now sometimes opens files by inode number rather than by path. This is enabled for wimcapture and wimappend, but for now other applications have to opt-in. The progress messages printed by wimlib-imagex while writing WIM files have been slightly tweaked. Progress information for directory tree scans now counts all hard links. Also, on Windows "\\?\" is no longer stripped from the current path. Added a new '--image-property' option to 'wimcapture', 'wimappend', and 'wiminfo'. This option lets you assign values to elements in a WIM file's XML document by name. The wimlib_get_image_property() and wimlib_set_image_property() API functions now support numerically indexed elements. Fixed a bug where, on Windows, wimlib would change the security descriptor of the target directory of an extraction even when the '--no-acls' option was specified. Version 1.8.3: Fixed a bug with libntfs-3g extraction present since v1.8.1. Sometimes, some Microsoft software would not correctly recognize data in the resulting filesystem. Made some small improvements to the compression algorithms: LZX compression ratio was slightly improved. XPRESS compression ratio and speed was slightly improved. LZMS compression speed was slightly improved. Improved handling of WIM XML data. wimlib no longer drops unrecognized elements when exporting images. In addition, two API functions were added for better access to elements in the XML document: wimlib_get_image_property() and wimlib_set_image_property(). Added support for (unsafe) in-place compaction of WIM files. Improved performance of image export by reusing metadata resources instead of always rebuilding and recompressing them. Improved performance of wimlib_update_image() by delaying the update to the WIM's XML document until a write is requested. On Windows, the target of an extraction may now be a reparse point (which will be dereferenced). On Windows, wimlib now correctly restores non-Microsoft reparse points. However, this remains broken in NTFS-3g mode due to a libntfs-3g bug. On Windows, wimlib now has improved performance when archiving files from a filesystem backed by a WIM (a "WIMBoot" setup). Several improvements to System Compression (compact mode) support: wof.sys (or wofadk.sys) is now automatically attached to the target volume if needed. Compact-mode extractions now work correctly with wofadk.sys on older versions of Windows. For compatibility with the Windows bootloader, the requested compression format now is overridden on certain files. Other minor bugfixes. Version 1.8.2: This release primarily contains various minor bug fixes and improvements, including: Improved handling of deep directory structures. Fixed a bug where on 32-bit systems, the library could enter an infinite loop if a WIM file was malformed in a specific way. Added a workaround for a case where libntfs-3g may report duplicate streams in an NTFS file. Windows symbolic links and junctions in mounted WIM images are now automatically rewritten to be valid in the mounted location. Reparse point fixes: correctly handle the "ReparseReserved" field, and correctly handle "empty" (data-less) reparse points. On Windows, wimlib now acquires SeManageVolumePrivilege, which is needed to create externally backed files using the "wofadk.sys" driver. Improved validation of filenames. Improved LZMS decompression speed. The configure script now honors alternate pkg-config settings. Links have been updated to point to the new website. In addition, experimental support has been added for compressing extracted files using System Compression on Windows 10. This functionality is available through the new '--compact' option to 'wimapply' and 'wimextract' as well as new library flags. Version 1.8.1: Fixed a bug in the LZX decompressor: malicious input data could cause out of bounds writes to memory (since wimlib v1.2.2). The output of the 'wiminfo' command now consolidates various boolean flags (such as "Relative path junction") into a single line. A file can now have both an unnamed data stream ("file contents") and a reparse point stream. Such files can exist as a result of the use of certain Windows features, such as offline storage, including "OneDrive". wimlib will now store and restore both streams on Windows as well as in NTFS-3g mode. Microsoft's WIMGAPI also has this behavior. On Windows, named data streams of encrypted files are no longer stored twice in WIM archives. On Windows, named data streams are now correctly extracted to existing "readonly" directories. Before, an error would be reported. On Windows, it is now possible to do a "WIMBoot mode" extraction with non-standalone WIMs such as delta WIMs. On Windows, when doing an extraction in "WIMBoot mode", files larger than 4 gigabytes are now never extracted as externally backed. This works around a bug in Microsoft's "WOF" driver. The '--enable-verify-compression' configure option has been removed. If you want to verify a WIM file, use the 'wimverify' program. The way the "file count", "directory count", "total bytes", and "hard link bytes" image statistics (stored in the WIM XML data) is calculated has been slightly changed. In mounted WIM images, the disk usage provided for each file (st_blocks) is now the compressed size rather than the uncompressed size. The performance of the NTFS-3g and Windows capture modes has been slightly improved. On UNIX-like systems, symbolic links whose targets contain the backslash character are now handled correctly (losslessly). Version 1.8.0: Improved the LZX compressor. It is now 15-20% faster than before and provides a slightly better compression ratio. Improved the LZMS compressor. It now provides a compression ratio slightly better than WIMGAPI while still being faster and using slightly less memory. The compression chunk size in solid resources, e.g. when capturing or exporting a WIM file using the '--solid' option, now defaults to 64 MiB (67108864 bytes) instead of 32 MiB (33554432 bytes). This provides a better compression ratio and is the same value that WIMGAPI uses. The memory usage is less than 50% higher than wimlib v1.7.4 and is slightly lower than WIMGAPI's memory usage, but if it is too much, it is still possible to choose a lower value, e.g. with the '--solid-chunk-size' option to wimlib-imagex. The '--chunk-size' and '--solid-chunk-size' options to wimlib-imagex now accept the 'K', 'M', and 'G' suffixes. Files are now sorted by name extension when creating a solid WIM file. Fixed various issues related to capture/apply of EFS-encrypted files on Windows. The file list printed by 'wimdir' is now sorted by the platform-specific case sensitivity setting, rather than always case sensitively. This also affects the library function wimlib_iterate_dir_tree(). On Windows, some error and warning messages have been improved. Version 1.7.4: The Windows binary distribution no longer contains third party DLLs. These dependencies are instead compiled directly into the libwim DLL. Added more fixes for wimlib on non-x86 architectures such as ARM. Extracting files to a Windows PE in-memory filesystem no longer fails if the target files do not yet exist. Improved the performance of XPRESS compression and LZMS decompression. Enabled SSSE3 accelerated SHA-1 computation in x86_64 Windows builds. It will automatically be faster on newer Intel and AMD processors. Removed the --with-imagex-progname and --enable-more-assertions configure options. Version 1.7.3: Fix for very slow export from solid WIM / ESD files. Fix for LZX and LZMS algorithms on non-x86 architectures, such as ARM. New progress message: WIMLIB_PROGRESS_MSG_HANDLE_ERROR. Applications may use this to treat some types of errors as non-fatal. The library now permits making in-memory changes to a WIMStruct backed by a read-only WIM file. Fixes for "WIMBoot" extraction mode (Windows only): When not using the WOF driver, extraction no longer fails if the disk containing the WIM file has too many partitions. When matching patterns in [PrepopulateList], all hard links of each file are now considered. The system registry files are now automatically treated as being in [PrepopulateList]. Added a hack to try to work around an intermittent bug in Microsoft's WOF (Windows Overlay Filesystem) driver. Version 1.7.2: Made more improvements to the XPRESS, LZX, and LZMS compressors. A number of improvements to the Windows port: Fixes for setting short filenames. Faster "WIMBoot" extraction. Updated and slimmed down the dependent DLLs. ACL inheritence bits are now restored. Mandatory integrity labels are now backed up and restored. Added a workaround for an issue where in rare cases, wimlib could create a compressed data stream that could not be read correctly by Windows after an extraction in "WIMBoot" mode. Library changes: Added file count progress data for WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE and WIMLIB_PROGRESS_MSG_EXTRACT_METADATA. Added support for testing file exclusions via the user-provided progress function. Some documentation improvements. Made some clarifications to the license text in the COPYING file. Version 1.7.1: Made more improvements to the XPRESS, LZX, and LZMS compressors. The default compression mode for wimcapture is now LZX compression in its default mode, which is the same as '--compress=maximum'. You can now specify an optional integer compression level to the '--compress' option; e.g. '--compress=lzx:75'. Made a minor change to the LZMS compressor and decompressor to fix an incompatibility with the Microsoft implementation. In the unlikely event that you created an LZMS-compressed WIM with wimlib v1.7.0 or earlier and a checksum error is reported when extracting files from it with wimlib v1.7.1, decompress it with v1.7.0 then compress it with v1.7.1. Added 'verify' subcommand to wimlib-imagex. Notable library changes: Custom compressor parameters have been removed from the library in favor of the simpler level-based API. Decompressor parameters have been removed entirely. Library users can now specify a custom file for warning and error messages to be sent to, rather than the default of standard error. New progress messages: WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE, WIMLIB_PROGRESS_MSG_EXTRACT_METADATA. New function: wimlib_verify_wim(). Version 1.7.0: Improved compression, decompression, and extraction performance. Improved compatibility with version 3584 WIM / ESD files: - Add support for reading and writing multiple solid blocks per archive, which WIMGAPI/DISM can create when appending an image. - Correctly create solid blocks larger than 4 GiB. 'add' commands passed to wimupdate will now replace existing nondirectory files by default. Use the --no-replace option to get the old behavior. The license for the library now contains an exception that allows using it under the LGPL. See the COPYING file for details. In reparse-point fixup mode (the default for capture), symbolic links and junctions that point outside the tree being captured are no longer excluded from capture. Added support for "WIMBoot" capture and extraction. See the documentation for the new '--wimboot' option to wimcapture and wimapply for more information. On UNIX-like systems, you can now backup and restore device nodes, named pipes, and sockets. In addition, 32-bit user and group IDs are now supported. The way that UNIX data is stored in WIM files has been changed. If you captured any WIMs with the --unix-data option, to upgrade them you'll need to apply them with --unix-data using wimlib-imagex v1.6.2, then re-capture them with --unix-data using this version. wimlib now understands tagged metadata items, such as object IDs, that can be stored in WIM directory entries. Removed the --hardlink and --symlink options to wimapply, since I don't think they are too useful and they got in the way of improving the code. WIMs will now retain their GUIDs when rebuilt (e.g. with wimoptimize). The 'mkwinpeimg' script now supports writing the ISO image to standard output. The element in WIM XML data is now exported correctly. On Windows, sparse file attributes are no longer set on extracted files. Oddly enough, this actually saves disk space in some cases. On UNIX, configuring with --disable-xattr or --enable-xattr is no longer supported. Mounting WIM images now always requires extended attribute support. Use --without-fuse to disable support for mounting WIM images; this will also disable the need for extended attribute support. Configuring with --enable-ssse3-sha1 now works correctly. The shared library version has been bumped up. The main incompatibilities are: - WIMLIB_COMPRESSION_TYPE_XPRESS is now 1 and WIMLIB_COMPRESSION_TYPE_LZX is now 2 (so it's the same as WIMGAPI). - User-provided progress functions are now registered using a separate function, wimlib_register_progress_function(). The 'progress_func' argument to many functions no longer exists. - The return value from user-provided progress functions is now significant. - A context argument has been added to the prototype of user-provided progress functions. - 'struct wimlib_capture_config' has been removed. The library now takes the path to the configuration file directly. This affects wimlib_add_image(), wimlib_add_image_multisource(), and wimlib_update_image(). However, a NULL value passed in the argument retains the same meaning. - Removed deprecated functions: some (de)compression functions, wimlib_extract_files(), and wimlib_print_metadata(). - Removed extraction flags: WIMLIB_EXTRACT_FLAG_HARDLINK, WIMLIB_EXTRACT_FLAG_SYMLINK, WIMLIB_EXTRACT_FLAG_FILE_ORDER, and WIMLIB_EXTRACT_FLAG_SEQUENTIAL. - Removed some progress messages: WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS, WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN, WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END. Numbering stays the same. - Removed some error codes. Numbering stays the same. - Replaced WIMLIB_UNMOUNT_FLAG_LAZY with WIMLIB_UNMOUNT_FLAG_FORCE. - WIM paths passed to progress functions now have a leading slash. Version 1.6.2: Case-insensitive comparisons of strings (e.g. filenames) containing UTF-16 codepoints above 32767 are now done correctly. Fixed build failure on Mac OS X. wimunmount now provides the '--new-image' option to cause changes to a read-write mounted image to be committed as a new image rather than as an update of the mounted image. (The corresponding new library flag is WIMLIB_UNMOUNT_FLAG_NEW_IMAGE.) The LZMS ("recovery") compression chunk size, or "dictionary size", may now be up to 1 GiB (1,073,741,824 bytes). The performance of LZX ("maximum") and LZMS ("recovery") compression with large chunk sizes has been slightly improved. Version 1.6.1: Stored files with size exactly 4 GiB (4,294,967,296 bytes) are now decompressed correctly. Fixed a bug in the LZX compressor introduced in v1.5.3. The bug occurred in an unlikely case, and due to validity checks it did not affect successfully created archives. Fixed a minor compatibility issue with the LZMS compressor and decompressor. This is *not* the default compression type and was only introduced in v1.6.0. In the unlikely event that you created an LZMS-compressed WIM with v1.6.0 and a checksum error is reported when applying it with v1.6.1, decompress it with v1.6.0 then compress it with v1.6.1. Memory usage for LZMS and LZX compression has been decreased. wimextract now allows wildcard characters in paths specified on the command line. Also, the '--strict-wildcards' option has been removed and replaced with the inverse option '--nullglob'. See the documentation for wimextract for more details and changes. The wimlib_extract_files() function is now considered deprecated in favor of wimlib_extract_paths(). Fixed more permissions problems when extracting files on Windows. A new '--no-attributes' option has been added to wimapply and wimextract. The library flag is WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES. The default chunk size is now set correctly when changing the compression type of a WIM, for example with 'wimoptimize'. The '--metadata' option to wiminfo has been replaced with the '--detailed' option to wimdir. In relevant wimlib-imagex commands, '--solid' may now be used as an alias for '--pack-streams'. Version 1.6.0: Support for extracting and updating the new version 3584 WIMs has been added. These WIMs typically pack many streams ("files") together into a single compressed resource, thereby saving space. This degrades the performance of random access (such as that which occurs on a mounted image), but optimizations have been implemented for extraction. These new WIM files also typically use a new compression format (LZMS), which is similar to LZMA and can offer a better compression ratio than LZX. These new WIM files can be created using `wimcapture' with the '--compress=lzms --pack-streams' options. Note: this new WIM format is used by the Windows 8 web downloader, but important segments of the raw '.esd' files are encrypted, so wimlib will not be able to extract such files until they are first decrypted. wimlib now supports extracting files and directories from a WIM image based on a "listfile" that itself contains the list of paths to extract. For `wimextract', the syntax is to specify @LISTFILE instead of a PATH, and for the library itself, the new APIs are wimlib_extract_pathlist() and wimlib_extract_paths(). Path globs containing wildcard characters are supported. For searching WIM files, wimlib now has configurable case sensitivity. The default on Windows is still case-insensitive and the default on UNIX-like systems is still case-sensitive, but this can be overridden on either platform through flags to wimlib_global_init(). For `wimlib-imagex', the environmental variable WIMLIB_IMAGEX_IGNORE_CASE can be set to 1 or 0 for case-insensitive or case-sensitive behavior, respectively. Support for compression chunk sizes greater than the default of 32768 bytes has been added. A larger chunk size typically results in a better compression ratio. However, the MS implementation is seemingly not compatible with all chunk sizes, especially for LZX compression, so the defaults remain unchanged, with the exception of the new LZMS-compressed WIMs, which use a larger chunk size by default. The compression/decompression API exported by wimlib has been changed. Now one set of functions handles all supported compression formats. `wimcapture' and `wimappend' will now display the progress of scanning the directory tree to capture, in addition to the progress of writing data to the WIM. The '--verbose' option no longer does anything. The library API change for this is the addition of several members to `struct wimlib_progress_info_scan' available to progress callbacks. `mkwinpeimg' now correctly handles the '--start-script' option when the start script is not in the working directory. Sequential extraction, previously requested by using WIMLIB_EXTRACT_FLAG_SEQUENTIAL, is now the default. WIMLIB_EXTRACT_FLAG_FILE_ORDER can be used to get the old default behavior (extract in file order). Version 1.5.3: The new LZX compressor added in v1.5.2 has been improved and is now enabled by default, except when `wimcapture' or `wimappend' is run *without* the '--compress' option, in which case the faster LZX compressor is used (the same as before). This behavior is reasonably consistent with ImageX which actually uses "fast" (XPRESS) compression by default. In those cases, use '--compress=maximum' to explicitly capture a WIM image using the new (slower but better) LZX compressor. The '--compress-slow' option still exists to `wimlib-imagex optimize', but its new behavior is to tweak the new LZX compressor even more to produce an even better compression ratio at the cost of more time spent compressing. `wimlib-imagex optimize' now supports the '--compress=TYPE' option, which recompresses the WIM file using the specified compression TYPE. The new library API function used for this is wimlib_set_output_compression_type(). Added the wimlib_get_xml_data() function to allow library clients to easily retrieve the raw XML data from a WIM file if needed. Fixed a bug that could cause an error code to be incorrectly returned when writing XML data containing a element. Mounted WIM images will now correctly show the default file stream even if appears in the alternate data stream entries of the corresponding WIM directory entry. Version 1.5.2: Added a new experimental LZX compressor which can be enabled by passing '--compress-slow' to `wimlib-imagex capture' or `wimlib-imagex optimize'. (The latter is only applicable if the WIM is already LZX-compressed and the '--recompress' option is also given.) The experimental compressor is much slower but compresses the data slightly more --- currently usually to within a fraction of a percent of the results from WIMGAPI/ImageX. A workaround has been added for compatibility with versions of WinPE that interpret alternate data stream entries in the boot WIM incorrectly. An alignment bug that caused a crash in the LZX decompressor on some builds was fixed. wimlib now attempts to clear the WIM_HDR_FLAG_WRITE_IN_PROGRESS flag in the WIM header when restoring the previous state of a WIM it failed to successfully update. Added a workaround to avoid an access denied error on Windows when replacing a WIM file that another process has opened. Version 1.5.1: wimlib can now open WinPE WIMs from WAIK v2.1, which had a quirk that needed to be handled. A bug in the interpretation of negative IMAGE indices in the --update-of=[WIMFILE:]IMAGE option to `wimlib-imagex capture' and `wimlib-imagex append' has been fixed. A workaround has been added to successfully apply security descriptors with empty DACLs when the NTFS-3g apply mode is being used with NTFS-3g 2013.1.13 or earlier. `wimlib-imagex capture' can now accept the '--delta-from' option multiple times. Version 1.5.0: Added support for "pipable" WIMs. Pipable WIMs allow capturing images to standard output and applying images from standard input, but they are not compatible with Microsoft's software and are not created by default. See the documentation for --pipable flag of `wimlib-imagex capture' for more information. To better support incremental backups, added support for declaring an image as a modified form of another image. See the documentation for the '--update-of' option of `wimlib-imagex append' and `wimlib-imagex capture'. Added supported for "delta" WIMs. See the documentation for the '--delta-from' option of `wimlib-imagex capture'. The library support for managing split WIMs has been changed to support other arrangements, such as delta WIMs, and be easier to use. This change is visible in `wimlib-imagex', which also can now accept the '--ref' option multiple times, and also now supports "delta" WIMs as mentioned above. wimlib now preserves WIM integrity tables by default, even if WIMLIB_WRITE_FLAG_CHECK_INTEGRITY is not specified. This changes the behavior of `wimlib-imagex' whenever the WIM being operated on contains an integrity table and the '--check' option is not specified. `wimlib-imagex capture' now creates LZX-compressed WIMs by default (when --compress is not specified). This provides the best compression ratio by default, which is usually what is desired, at a cost of some speed. `wimlib-imagex' now supports being invoked as wimCOMMAND, where COMMAND is the command as in `wimlib-imagex COMMAND'; for example, it can be invoked as `wimapply' as an alternative to `wimlib-imagex apply'. The appropriate hard links are created in UNIX installations of `wimlib-imagex', while for the Windows distribution of `wimlib-imagex', batch files that emulate this behavior are generated. Security descriptors are now extracted correctly on Windows. Fixed archiving DOS names in NTFS-3g capture mode. The extraction code has been rewritten and it will now be easier to support new features on all supported backends (currently Win32, UNIX, and NTFS-3g). For example, hard-linked extraction mode (--hardlink) is now supported on all backends, not just UNIX. `mkwinpeimg' now supports grabbing files from the WAIK supplement rather than the WAIK itself. wimlib_global_init() now, by default, attempts to acquire additional privileges on Windows, so library clients need not do this. This update bumps the shared library version number up to 9, since it is not binary compatibible with previous releases. Version 1.4.2: Fixed bug in `wimlib-imagex export' that made it impossible to export an image from a WIM that is readonly at the filesystem level. Return error code rather than segfaulting when trying to list files from a non-first part of a split WIM. Joining a WIM will now preserve the RP_FIX and READONLY flags. Version 1.4.1: On Windows, paths given to wimlib-imagex are now treated case insensitively. Improved behavior regarding invalid filenames; in particular, on Windows, wimlib-imagex will, when extracting, now omit (with an option to override this default) filenames differing only in case, or filenames containing characters not valid on Windows. On Windows, wimlib now supports capturing and extracting long paths (longer than the so-called MAX_PATH). On Windows, `wimlib-imagex update' now acquires proper privileges when running as an Administrator. `wimlib-imagex update' will now complain if no image is specified when trying to update a multi-image WIM. `wimlib-imagex update' now supports specifying a single update command directly on the command line using the --command option. wimlib-imagex will now choose different units for progress messages, depending on the amount of data that needs to be processed. `wimlib-imagex append' will now generate a unique WIM image name if no name is specified and the defaulted name already exists in the WIM. wimlib now allows you to create unnamed WIM images, which can then only be referred to by index. wimlib now allows you to explicitly declare you want write access to a WIM by providing the WIMLIB_OPEN_FLAG_WRITE_ACCESS flag to wimlib_open_wim(). wimlib now respects the WIM_HDR_FLAG_READONLY flag when set in the WIM header. Progress callbacks have been added to wimlib's wimlib_update_image() function. Added wimlib_get_wim_info(), wimlib_set_wim_info(), wimlib_iterate_dir_tree(), and wimlib_iterate_lookup_table() functions to the library. NTFS-3g capture now only warns about two conditions previously treated as errors. Fixed a couple issues with using wimlib-imagex on UDF filesystems on Windows. wimlib now correctly detects and returns an error when reading a WIM image with a cyclic directory structure. (Fun fact: such a WIM will crash Microsoft's software.) Version 1.4.0: Added new "extract" and "update" subcommands to wimlib-imagex, along with associated APIs in the library. These commands are intended mainly for Windows use but can be used on UNIX as well. Many documentation improvements. Fixed a bug in the Windows build where relative symbolic links were not captured in reparse-point fixup mode. Fixed a bug in the Windows build where file handles were left open to the WIM file, causing `wimlib_imagex optimize' to fail in some cases. Fixed a bug in the Windows build of wimlib-imagex where globbing split-WIM parts could cause the program to crash. Fixed a bug where the creation time of WIM images would be shown instead of the last modification time. With the Windows build it is now possible to restore a WIM containing symbolic links as a non-Administrator; however you will receive warnings about not being able to extract the symbolic links. Version 1.3.3: Capturing a WIM image should now be significantly faster in most cases due to improved use of the operating system's cache and avoiding reading files twice whenever possible. The Windows build should now work on Windows XP. The Windows build now supports capturing and restoring hidden, compressed, sparse, and encrypted files. The Windows build now supports capturing and applying WIM images from filesystems other than NTFS (with some reduced functionality). The Windows build now extracts short names correctly. Added support for "reparse-point" fixups (i.e. fixing up of symbolic links). See docs for --rpfix and --norpfix flags of `wimlib-imagex capture' and `wimlib-imagex apply'. The performance of splitting and joining WIMs should be slightly improved. The LZX and XPRESS compression and decompression functions are now exported from the library. Version 1.3.2: Improvements and bugfixes for the Windows build. Added --strict-acls options. Fixed the way that wimlib determines the order of images in the WIM. Version 1.3.1: Since wimlib can now be used on Windows, wimlib's implementation of ImageX has been renamed to wimlib-imagex to avoid confusion with Microsoft's implementation of ImageX, which would have the same file name ("imagex.exe"). If you really don't like this you can pass the --with-imagex-progname option to `configure' to choose a different name, or even simply rename the binary yourself (but the former way will configure the man pages to use the chosen name). Various bugs fixed in the Windows build. Mainly to do with capturing and restoring alternate data streams correctly in weird cases, and requesting the correct privileges when opening files. Also added the --noacls options to wimlib-imagex capture, append, and apply. Windows build again: FindFirstStreamW() and FindNextStreamW() are now dynamically loaded, so this may make the library compatible with Windows XP (however, there may still be other problems). Version 1.3.0: Added experimental support for native Windows builds. Binaries can be downloaded from the SourceForge page. --source-list option added to `imagex capture' and `imagex append'. Better support for different character encodings. Version 1.2.6: Storing UNIX file owners, groups, and modes in WIM images is now possible using `imagex capture' with the --unix-data flag. Minor bug fixes and documentation fixes. Version 1.2.5: NTFS capture: Fixed capturing duplicate reparse points. NTFS capture: Capture first unnamed stream if there are more than one (print warning instead of error). Allow multiple test cases to execute concurrently (e.g. make -j2 check). Version 1.2.4: Added --arch switch to mkwinpeimg script to support getting AMD64 WinPE from the WAIK. Update to work with ntfs-3g version 2013.1.13. Version 1.2.3: Fixed truncating file to shorter but non-zero length on read-write mounted WIM image. Various code cleanups and minor documentation fixes. Version 1.2.2: LZX and XPRESS decompression have received some additional optimizations and should now be even faster. (Although, they were already pretty fast--- much faster than typical I/O speeds.) Fixed a bug introduced in v1.2.1 that would cause a directory tree containing hard links to be captured incorrectly in some cases. Version 1.2.1: By default, unmounting a read-write mounted WIM with 'imagex unmount --commit' will now change the WIM in-place without needing to write the entire WIM again. Use 'imagex unmount --commit --rebuild' to get the old behavior. 'imagex unmount' no longer has a hard-coded limit of 10 minutes to wait for a response from the daemon servicing the mounted WIM. Instead, every second 'imagex unmount' will check if the daemon is still alive, and keep waiting if so, otherwise terminate with an error. 'imagex unmount --commit' on a read-write mounted WIM will now print progress information regarding the writing of new or modified streams the WIM, just like when capturing or appending a WIM. A small change has been made to XPRESS compression and it should improve the compression ratio slightly. A change was made that may improve performance slightly when applying a WIM image to a NTFS volume. Microsoft has managed to introduce even more bugs into their software, and now the WIMs for Windows 8 have incorrect (too low) reference counts for some streams. This is unsafe because such streams can be removed when they are in actuality still referenced in the WIM (perhaps by a different image). wimlib will now work around this problem by fixing the stream reference counts. This is only done when wimlib_delete_image() is called ('imagex delete') or when wimlib_mount_image() is called with WIMLIB_MOUNT_FLAG_READWRITE ('imagex mountrw'). Please note that this requires reading the metadata for all images in the WIM, so this will make these operations noticably slower on WIMs with multiple images. Various other bugfixes. Version 1.2.0: Appending images to a WIM is now be done by default without re-building the whole WIM. Use the --rebuild flag to get the old behavior (which was to re-build the entire WIM when a new image is appended). A new command `imagex optimize' is now available to manually re-build a WIM that has wasted space due to repeated appends. Progress information has been improved, and now arbitrary callback functions can be used to show the progress of a WIM operation. A possible bug with changing the bootable image of a WIM was fixed. Some advisory locking is now done to prevent two processes from modifying a WIM at the same time (but only in some cases). For example, you cannot mount two images from a WIM read-write at the same time. Some functions have been reorganized: * wimlib_mount() renamed to wimlib_mount_image(). * wimlib_unmount() renamed to wimlib_unmount_image(). * wimlib_overwrite_xml_and_header() removed as wimlib_overwrite() suffices now. * wimlib_apply_image_to_ntfs_volume() removed as wimlib_extract_image() suffices now. * wimlib_add_image_from_ntfs_volume() removed as * wimlib_add_image() suffices now. Previously, the soname of libwim.so has been 0.0.0, despite many interface changes. The soname is now updated to 1.0.0 and will now be updated each release. Version 1.1.0: Resources will now be compressed using multiple threads by default. (This applies to `imagex capture', `imagex append', and `imagex export'). Some performance improvements in mounted WIMs. More progress information is shown when capturing a WIM. Version 1.0.4: Lots of minor fixes, code cleanups, and some documentation updates. Nothing in particular is really noteworthy. Version 1.0.3: LZX and XPRESS compression improvements. Fixed calculation of Directory Count, File Count, Total Bytes, and Hard Link Bytes of the WIM. Version 1.0.2: Fixed bug when capturing NTFS file with multiple named data streams. Internally, we are now using inode structures, even though these don't appear literally in the WIM file. This simplifies some of the code (mainly for WIM mounting) and likely fixed a few problems, although it needs more testing. Version 1.0.1: Fixed problem when exporting images from XPRESS to LZX compressed WIM or vice versa Version 1.0.0: Enough changes to call it version 1.0.0! Capturing a WIM directly from a NTFS volume, and applying a WIM directly to a NTFS volume, is now supported. Hard links and symbolic links have much improved support. They are supported for WIM capture, WIM application, and mounted WIMs (you can even make them on read-write mounted WIMs). Alternate data streams are now supported on mounted WIMs through an xattr or a Windows-style stream interface. Also they are supported when capturing a WIM from NTFS or applying a WIM to NTFS. Split WIMs are better supported. You may now apply an image directly from a split WIM, mount an image from a split WIM read-only, or export an image from a split WIM. Using a capture configuration file is now supported (but not fully yet). SHA1 message digests are checked in more places, so we can make sure applied and captured data is correct. Man pages have been updated and consolidated. Version 0.7.2: Fixed segfault when unmounting read-only WIM. Version 0.7.1: Support for joining and splitting WIMs. Also, security data is now preserved by default. Version 0.6.3: Can now build with older gcc and system headers, like on CentOS 5. Version 0.6.2: Fixed bug that made it impossible to overwrite files in read-write mount. Version 0.6.1: Write byte-order mark before WIM XML data. (imagex.exe requires this to be there.) wimlib-1.13.1/programs/0000755000175000017500000000000013464166632011726 500000000000000wimlib-1.13.1/programs/imagex.c0000644000175000017500000041274513345631265013276 00000000000000/* * imagex.c * * Use wimlib to create, modify, extract, mount, unmount, or display information * about a WIM file */ /* * Copyright (C) 2012-2018 Eric Biggers * * 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 Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifdef HAVE_CONFIG_H # include "config.h" /* Need for PACKAGE_VERSION, etc. */ #endif #include "wimlib.h" #include "wimlib_tchar.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_ALLOCA_H # include #endif #define WIMLIB_COMPRESSION_TYPE_INVALID (-1) #ifdef __WIN32__ # include "imagex-win32.h" # define print_security_descriptor win32_print_security_descriptor #else /* __WIN32__ */ # include # include # define print_security_descriptor default_print_security_descriptor static inline void set_fd_to_binary_mode(int fd) { } /* NetBSD is missing getopt_long_only() but has getopt_long() */ #ifndef HAVE_GETOPT_LONG_ONLY # define getopt_long_only getopt_long #endif #endif /* !__WIN32 */ /* Don't confuse the user by presenting the mounting commands on Windows when * they will never work. However on UNIX-like systems we always present them, * even if WITH_FUSE is not defined at this point, as to not tie the build of * wimlib-imagex to a specific build of wimlib. */ #ifdef __WIN32__ # define WIM_MOUNTING_SUPPORTED 0 #else # define WIM_MOUNTING_SUPPORTED 1 #endif #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0])) static inline bool is_any_path_separator(tchar c) { return c == T('/') || c == T('\\'); } /* Like basename(), but handles both forward and backwards slashes. */ static tchar * tbasename(tchar *path) { tchar *p = tstrchr(path, T('\0')); for (;;) { if (p == path) return path; if (!is_any_path_separator(*--p)) break; *p = T('\0'); } for (;;) { if (p == path) return path; if (is_any_path_separator(*--p)) return ++p; } } #define for_opt(c, opts) while ((c = getopt_long_only(argc, (tchar**)argv, T(""), \ opts, NULL)) != -1) enum { CMD_NONE = -1, CMD_APPEND = 0, CMD_APPLY, CMD_CAPTURE, CMD_DELETE, CMD_DIR, CMD_EXPORT, CMD_EXTRACT, CMD_INFO, CMD_JOIN, #if WIM_MOUNTING_SUPPORTED CMD_MOUNT, CMD_MOUNTRW, #endif CMD_OPTIMIZE, CMD_SPLIT, #if WIM_MOUNTING_SUPPORTED CMD_UNMOUNT, #endif CMD_UPDATE, CMD_VERIFY, CMD_MAX, }; static void usage(int cmd, FILE *fp); static void usage_all(FILE *fp); static void recommend_man_page(int cmd, FILE *fp); static const tchar *get_cmd_string(int cmd, bool only_short_form); static FILE *imagex_info_file; #define imagex_printf(format, ...) \ if (imagex_info_file) \ tfprintf(imagex_info_file, format, ##__VA_ARGS__) static void imagex_suppress_output(void) { imagex_info_file = NULL; } static void imagex_output_to_stderr(void) { if (imagex_info_file) imagex_info_file = stderr; } static void imagex_flush_output(void) { if (imagex_info_file) fflush(imagex_info_file); } enum { IMAGEX_ALLOW_OTHER_OPTION, IMAGEX_BLOBS_OPTION, IMAGEX_BOOT_OPTION, IMAGEX_CHECK_OPTION, IMAGEX_CHUNK_SIZE_OPTION, IMAGEX_COMMAND_OPTION, IMAGEX_COMMIT_OPTION, IMAGEX_COMPACT_OPTION, IMAGEX_COMPRESS_OPTION, IMAGEX_CONFIG_OPTION, IMAGEX_CREATE_OPTION, IMAGEX_DEBUG_OPTION, IMAGEX_DELTA_FROM_OPTION, IMAGEX_DEREFERENCE_OPTION, IMAGEX_DEST_DIR_OPTION, IMAGEX_DETAILED_OPTION, IMAGEX_EXTRACT_XML_OPTION, IMAGEX_FLAGS_OPTION, IMAGEX_FORCE_OPTION, IMAGEX_HEADER_OPTION, IMAGEX_IMAGE_PROPERTY_OPTION, IMAGEX_INCLUDE_INTEGRITY_OPTION, IMAGEX_INCLUDE_INVALID_NAMES_OPTION, IMAGEX_LAZY_OPTION, IMAGEX_METADATA_OPTION, IMAGEX_NEW_IMAGE_OPTION, IMAGEX_NOCHECK_OPTION, IMAGEX_NORPFIX_OPTION, IMAGEX_NOT_PIPABLE_OPTION, IMAGEX_NO_ACLS_OPTION, IMAGEX_NO_ATTRIBUTES_OPTION, IMAGEX_NO_GLOBS_OPTION, IMAGEX_NO_REPLACE_OPTION, IMAGEX_NO_SOLID_SORT_OPTION, IMAGEX_NULLGLOB_OPTION, IMAGEX_ONE_FILE_ONLY_OPTION, IMAGEX_PATH_OPTION, IMAGEX_PIPABLE_OPTION, IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION, IMAGEX_REBUILD_OPTION, IMAGEX_RECOMPRESS_OPTION, IMAGEX_RECURSIVE_OPTION, IMAGEX_REF_OPTION, IMAGEX_RPFIX_OPTION, IMAGEX_SNAPSHOT_OPTION, IMAGEX_SOFT_OPTION, IMAGEX_SOLID_CHUNK_SIZE_OPTION, IMAGEX_SOLID_COMPRESS_OPTION, IMAGEX_SOLID_OPTION, IMAGEX_SOURCE_LIST_OPTION, IMAGEX_STAGING_DIR_OPTION, IMAGEX_STREAMS_INTERFACE_OPTION, IMAGEX_STRICT_ACLS_OPTION, IMAGEX_THREADS_OPTION, IMAGEX_TO_STDOUT_OPTION, IMAGEX_UNIX_DATA_OPTION, IMAGEX_UNSAFE_COMPACT_OPTION, IMAGEX_UPDATE_OF_OPTION, IMAGEX_VERBOSE_OPTION, IMAGEX_WIMBOOT_CONFIG_OPTION, IMAGEX_WIMBOOT_OPTION, IMAGEX_XML_OPTION, }; static const struct option apply_options[] = { {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION}, {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION}, {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION}, {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION}, {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION}, {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION}, {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION}, {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION}, {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION}, {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION}, {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION}, {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION}, {NULL, 0, NULL, 0}, }; static const struct option capture_or_append_options[] = { {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION}, {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION}, {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION}, {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION}, {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION}, {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION}, {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION}, {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION}, {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION}, {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION}, {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION}, {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION}, {T("flags"), required_argument, NULL, IMAGEX_FLAGS_OPTION}, {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION}, {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION}, {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION}, {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION}, {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION}, {T("source-list"), no_argument, NULL, IMAGEX_SOURCE_LIST_OPTION}, {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION}, {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION}, {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION}, {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION}, {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION}, {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION}, {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION}, {T("update-of"), required_argument, NULL, IMAGEX_UPDATE_OF_OPTION}, {T("delta-from"), required_argument, NULL, IMAGEX_DELTA_FROM_OPTION}, {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION}, {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION}, {T("snapshot"), no_argument, NULL, IMAGEX_SNAPSHOT_OPTION}, {T("create"), no_argument, NULL, IMAGEX_CREATE_OPTION}, {NULL, 0, NULL, 0}, }; static const struct option delete_options[] = { {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION}, {T("soft"), no_argument, NULL, IMAGEX_SOFT_OPTION}, {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION}, {NULL, 0, NULL, 0}, }; static const struct option dir_options[] = { {T("path"), required_argument, NULL, IMAGEX_PATH_OPTION}, {T("detailed"), no_argument, NULL, IMAGEX_DETAILED_OPTION}, {T("one-file-only"), no_argument, NULL, IMAGEX_ONE_FILE_ONLY_OPTION}, {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION}, {NULL, 0, NULL, 0}, }; static const struct option export_options[] = { {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION}, {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION}, {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION}, {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION}, {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION}, {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION}, {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION}, {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION}, {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION}, {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION}, {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION}, {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION}, {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION}, {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION}, {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION}, {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION}, {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION}, {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION}, {NULL, 0, NULL, 0}, }; static const struct option extract_options[] = { {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION}, {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION}, {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION}, {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION}, {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION}, {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION}, {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION}, {T("dest-dir"), required_argument, NULL, IMAGEX_DEST_DIR_OPTION}, {T("to-stdout"), no_argument, NULL, IMAGEX_TO_STDOUT_OPTION}, {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION}, {T("no-wildcards"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION}, {T("no-globs"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION}, {T("nullglob"), no_argument, NULL, IMAGEX_NULLGLOB_OPTION}, {T("preserve-dir-structure"), no_argument, NULL, IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION}, {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION}, {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION}, {NULL, 0, NULL, 0}, }; static const struct option info_options[] = { {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION}, {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION}, {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION}, {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION}, {T("extract-xml"), required_argument, NULL, IMAGEX_EXTRACT_XML_OPTION}, {T("header"), no_argument, NULL, IMAGEX_HEADER_OPTION}, {T("lookup-table"), no_argument, NULL, IMAGEX_BLOBS_OPTION}, {T("blobs"), no_argument, NULL, IMAGEX_BLOBS_OPTION}, {T("xml"), no_argument, NULL, IMAGEX_XML_OPTION}, {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION}, {NULL, 0, NULL, 0}, }; static const struct option join_options[] = { {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION}, {NULL, 0, NULL, 0}, }; #if WIM_MOUNTING_SUPPORTED static const struct option mount_options[] = { {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, {T("debug"), no_argument, NULL, IMAGEX_DEBUG_OPTION}, {T("streams-interface"), required_argument, NULL, IMAGEX_STREAMS_INTERFACE_OPTION}, {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION}, {T("staging-dir"), required_argument, NULL, IMAGEX_STAGING_DIR_OPTION}, {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION}, {T("allow-other"), no_argument, NULL, IMAGEX_ALLOW_OTHER_OPTION}, {NULL, 0, NULL, 0}, }; #endif static const struct option optimize_options[] = { {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION}, {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION}, {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION}, {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION}, {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION}, {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION}, {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION}, {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION}, {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION}, {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION}, {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION}, {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION}, {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION}, {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION}, {NULL, 0, NULL, 0}, }; static const struct option split_options[] = { {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION}, {NULL, 0, NULL, 0}, }; #if WIM_MOUNTING_SUPPORTED static const struct option unmount_options[] = { {T("commit"), no_argument, NULL, IMAGEX_COMMIT_OPTION}, {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION}, {T("lazy"), no_argument, NULL, IMAGEX_LAZY_OPTION}, {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION}, {T("new-image"), no_argument, NULL, IMAGEX_NEW_IMAGE_OPTION}, {NULL, 0, NULL, 0}, }; #endif static const struct option update_options[] = { /* Careful: some of the options here set the defaults for update * commands, but the flags given to an actual update command (and not to * `imagex update' itself are also handled in * update_command_add_option(). */ {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION}, {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION}, {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION}, {T("command"), required_argument, NULL, IMAGEX_COMMAND_OPTION}, {T("wimboot-config"), required_argument, NULL, IMAGEX_WIMBOOT_CONFIG_OPTION}, /* Default delete options */ {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION}, {T("recursive"), no_argument, NULL, IMAGEX_RECURSIVE_OPTION}, /* Global add option */ {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION}, /* Default add options */ {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION}, {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION}, {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION}, {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION}, {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION}, {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION}, {T("no-replace"), no_argument, NULL, IMAGEX_NO_REPLACE_OPTION}, {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION}, {NULL, 0, NULL, 0}, }; static const struct option verify_options[] = { {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION}, {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION}, {NULL, 0, NULL, 0}, }; #if 0 # define _format_attribute(type, format_str, args_start) \ __attribute__((format(type, format_str, args_start))) #else # define _format_attribute(type, format_str, args_start) #endif /* Print formatted error message to stderr. */ static void _format_attribute(printf, 1, 2) imagex_error(const tchar *format, ...) { va_list va; va_start(va, format); tfputs(T("ERROR: "), stderr); tvfprintf(stderr, format, va); tputc(T('\n'), stderr); va_end(va); } /* Print formatted error message to stderr. */ static void _format_attribute(printf, 1, 2) imagex_error_with_errno(const tchar *format, ...) { int errno_save = errno; va_list va; va_start(va, format); tfputs(T("ERROR: "), stderr); tvfprintf(stderr, format, va); tfprintf(stderr, T(": %"TS"\n"), tstrerror(errno_save)); va_end(va); } static int verify_image_exists(int image, const tchar *image_name, const tchar *wim_name) { if (image == WIMLIB_NO_IMAGE) { imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\"!\n" " Please specify a 1-based image index or " "image name. To list the images\n" " contained in the WIM archive, run\n" "\n" " %"TS" \"%"TS"\"\n"), image_name, wim_name, get_cmd_string(CMD_INFO, false), wim_name); return WIMLIB_ERR_INVALID_IMAGE; } return 0; } static int verify_image_is_single(int image) { if (image == WIMLIB_ALL_IMAGES) { imagex_error(T("Cannot specify all images for this action!")); return WIMLIB_ERR_INVALID_IMAGE; } return 0; } static int verify_image_exists_and_is_single(int image, const tchar *image_name, const tchar *wim_name) { int ret; ret = verify_image_exists(image, image_name, wim_name); if (ret == 0) ret = verify_image_is_single(image); return ret; } static void print_available_compression_types(FILE *fp) { static const tchar * const s = T( "Available compression types:\n" "\n" " none\n" " xpress (alias: \"fast\")\n" " lzx (alias: \"maximum\") (default for capture)\n" " lzms (alias: \"recovery\")\n" "\n" ); tfputs(s, fp); } /* Parse the argument to --compress or --solid-compress */ static int get_compression_type(tchar *optarg, bool solid) { int ctype; unsigned int compression_level = 0; tchar *plevel; plevel = tstrchr(optarg, T(':')); if (plevel) { tchar *ptmp; unsigned long ultmp; *plevel++ = T('\0'); ultmp = tstrtoul(plevel, &ptmp, 10); if (ultmp >= UINT_MAX || ultmp == 0 || *ptmp || ptmp == plevel) { imagex_error(T("Compression level must be a positive integer! " "e.g. --compress=lzx:80")); return WIMLIB_COMPRESSION_TYPE_INVALID; } compression_level = ultmp; } if (!tstrcasecmp(optarg, T("maximum")) || !tstrcasecmp(optarg, T("lzx")) || !tstrcasecmp(optarg, T("max"))) { ctype = WIMLIB_COMPRESSION_TYPE_LZX; } else if (!tstrcasecmp(optarg, T("fast")) || !tstrcasecmp(optarg, T("xpress"))) { ctype = WIMLIB_COMPRESSION_TYPE_XPRESS; } else if (!tstrcasecmp(optarg, T("recovery"))) { if (!solid) { tfprintf(stderr, T( "Warning: use of '--compress=recovery' is discouraged because it behaves\n" " differently from DISM. Instead, you typically want to use '--solid' to\n" " create a solid LZMS-compressed WIM or \"ESD file\", similar to DISM's\n" " /compress:recovery. But if you really want *non-solid* LZMS compression,\n" " then you may suppress this warning by specifying '--compress=lzms' instead\n" " of '--compress=recovery'.\n")); } ctype = WIMLIB_COMPRESSION_TYPE_LZMS; } else if (!tstrcasecmp(optarg, T("lzms"))) { ctype = WIMLIB_COMPRESSION_TYPE_LZMS; } else if (!tstrcasecmp(optarg, T("none"))) { ctype = WIMLIB_COMPRESSION_TYPE_NONE; } else { imagex_error(T("Invalid compression type \"%"TS"\"!"), optarg); print_available_compression_types(stderr); return WIMLIB_COMPRESSION_TYPE_INVALID; } if (compression_level != 0) wimlib_set_default_compression_level(ctype, compression_level); return ctype; } /* Parse the argument to --compact */ static int set_compact_mode(const tchar *arg, int *extract_flags) { int flag = 0; if (!tstrcasecmp(arg, T("xpress4k"))) flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K; else if (!tstrcasecmp(arg, T("xpress8k"))) flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K; else if (!tstrcasecmp(arg, T("xpress16k"))) flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K; else if (!tstrcasecmp(arg, T("lzx"))) flag = WIMLIB_EXTRACT_FLAG_COMPACT_LZX; if (flag) { *extract_flags |= flag; return 0; } imagex_error(T( "\"%"TS"\" is not a recognized System Compression format. The options are:" "\n" " --compact=xpress4k\n" " --compact=xpress8k\n" " --compact=xpress16k\n" " --compact=lzx\n" ), arg); return -1; } struct string_list { tchar **strings; unsigned num_strings; unsigned num_alloc_strings; }; #define STRING_LIST_INITIALIZER \ { .strings = NULL, .num_strings = 0, .num_alloc_strings = 0, } #define STRING_LIST(_strings) \ struct string_list _strings = STRING_LIST_INITIALIZER static int string_list_append(struct string_list *list, tchar *glob) { unsigned num_alloc_strings = list->num_alloc_strings; if (list->num_strings == num_alloc_strings) { tchar **new_strings; num_alloc_strings += 4; new_strings = realloc(list->strings, sizeof(list->strings[0]) * num_alloc_strings); if (!new_strings) { imagex_error(T("Out of memory!")); return -1; } list->strings = new_strings; list->num_alloc_strings = num_alloc_strings; } list->strings[list->num_strings++] = glob; return 0; } static void string_list_destroy(struct string_list *list) { free(list->strings); } static int wim_reference_globs(WIMStruct *wim, struct string_list *list, int open_flags) { return wimlib_reference_resource_files(wim, (const tchar **)list->strings, list->num_strings, WIMLIB_REF_FLAG_GLOB_ENABLE, open_flags); } static int append_image_property_argument(struct string_list *image_properties) { if (!tstrchr(optarg, '=')) { imagex_error(T("'--image-property' argument " "must be in the form NAME=VALUE")); return -1; } return string_list_append(image_properties, optarg); } static int apply_image_properties(struct string_list *image_properties, WIMStruct *wim, int image, bool *any_changes_ret) { bool any_changes = false; for (unsigned i = 0; i < image_properties->num_strings; i++) { tchar *name, *value; const tchar *current_value; int ret; name = image_properties->strings[i]; value = tstrchr(name, '='); *value++ = '\0'; current_value = wimlib_get_image_property(wim, image, name); if (current_value && !tstrcmp(current_value, value)) { imagex_printf(T("The %"TS" property of image %d " "already has value \"%"TS"\".\n"), name, image, value); } else { imagex_printf(T("Setting the %"TS" property of image " "%d to \"%"TS"\".\n"), name, image, value); ret = wimlib_set_image_property(wim, image, name, value); if (ret) return ret; any_changes = true; } } if (any_changes_ret) *any_changes_ret = any_changes; return 0; } static void do_resource_not_found_warning(const tchar *wimfile, const struct wimlib_wim_info *info, const struct string_list *refglobs) { if (info->total_parts > 1) { if (refglobs->num_strings == 0) { imagex_error(T("\"%"TS"\" is part of a split WIM. " "Use --ref to specify the other parts."), wimfile); } else { imagex_error(T("Perhaps the '--ref' argument did not " "specify all other parts of the split " "WIM?")); } } else { imagex_error(T("If this is a delta WIM, use the --ref argument " "to specify the WIM(s) on which it is based.")); } } static void do_metadata_not_found_warning(const tchar *wimfile, const struct wimlib_wim_info *info) { if (info->part_number != 1) { imagex_error(T("\"%"TS"\" is not the first part of the split WIM.\n" " You must specify the first part."), wimfile); } } /* Returns the size of a file given its name, or -1 if the file does not exist * or its size cannot be determined. */ static off_t file_get_size(const tchar *filename) { struct stat st; if (tstat(filename, &st) == 0) return st.st_size; else return (off_t)-1; } enum { PARSE_STRING_SUCCESS = 0, PARSE_STRING_FAILURE = 1, PARSE_STRING_NONE = 2, }; /* * Parses a string token from an array of characters. * * Tokens are either whitespace-delimited, or double or single-quoted. * * @line_p: Pointer to the pointer to the line of data. Will be updated * to point past the string token iff the return value is * PARSE_STRING_SUCCESS. If *len_p > 0, (*line_p)[*len_p - 1] must * be '\0'. * * @len_p: @len_p initially stores the length of the line of data, which may * be 0, and it will be updated to the number of bytes remaining in * the line iff the return value is PARSE_STRING_SUCCESS. * * @fn_ret: Iff the return value is PARSE_STRING_SUCCESS, a pointer to the * parsed string token will be returned here. * * Returns: PARSE_STRING_SUCCESS if a string token was successfully parsed; or * PARSE_STRING_FAILURE if the data was invalid due to a missing * closing quote; or PARSE_STRING_NONE if the line ended before the * beginning of a string token was found. */ static int parse_string(tchar **line_p, size_t *len_p, tchar **fn_ret) { size_t len = *len_p; tchar *line = *line_p; tchar *fn; tchar quote_char; /* Skip leading whitespace */ for (;;) { if (len == 0) return PARSE_STRING_NONE; if (!istspace(*line) && *line != T('\0')) break; line++; len--; } quote_char = *line; if (quote_char == T('"') || quote_char == T('\'')) { /* Quoted string */ line++; len--; fn = line; line = tmemchr(line, quote_char, len); if (!line) { imagex_error(T("Missing closing quote: %"TS), fn - 1); return PARSE_STRING_FAILURE; } } else { /* Unquoted string. Go until whitespace. Line is terminated * by '\0', so no need to check 'len'. */ fn = line; do { line++; } while (!istspace(*line) && *line != T('\0')); } *line = T('\0'); len -= line - fn; *len_p = len; *line_p = line; *fn_ret = fn; return PARSE_STRING_SUCCESS; } /* Parses a line of data (not an empty line or comment) in the source list file * format. (See the man page for 'wimlib-imagex capture' for details on this * format and the meaning.) * * @line: Line of data to be parsed. line[len - 1] must be '\0', unless * len == 0. The data in @line will be modified by this function call. * * @len: Length of the line of data. * * @source: On success, the capture source and target described by the line is * written into this destination. Note that it will contain pointers * to data in the @line array. * * Returns true if the line was valid; false otherwise. */ static bool parse_source_list_line(tchar *line, size_t len, struct wimlib_capture_source *source) { /* SOURCE [DEST] */ int ret; ret = parse_string(&line, &len, &source->fs_source_path); if (ret != PARSE_STRING_SUCCESS) return false; ret = parse_string(&line, &len, &source->wim_target_path); if (ret == PARSE_STRING_NONE) source->wim_target_path = source->fs_source_path; return ret != PARSE_STRING_FAILURE; } /* Returns %true if the given line of length @len > 0 is a comment or empty line * in the source list file format. */ static bool is_comment_line(const tchar *line, size_t len) { for (;;) { if (*line == T('#') || *line == T(';')) return true; if (!istspace(*line) && *line != T('\0')) return false; ++line; --len; if (len == 0) return true; } } static ssize_t text_file_count_lines(tchar **contents_p, size_t *nchars_p) { ssize_t nlines = 0; tchar *contents = *contents_p; size_t nchars = *nchars_p; size_t i; for (i = 0; i < nchars; i++) if (contents[i] == T('\n')) nlines++; /* Handle last line not terminated by a newline */ if (nchars != 0 && contents[nchars - 1] != T('\n')) { contents = realloc(contents, (nchars + 1) * sizeof(tchar)); if (!contents) { imagex_error(T("Out of memory!")); return -1; } contents[nchars] = T('\n'); *contents_p = contents; nchars++; nlines++; } *nchars_p = nchars; return nlines; } /* Parses a file in the source list format. (See the man page for * 'wimlib-imagex capture' for details on this format and the meaning.) * * @source_list_contents: Contents of the source list file. Note that this * buffer will be modified to save memory allocations, * and cannot be freed until the returned array of * wimlib_capture_source's has also been freed. * * @source_list_nbytes: Number of bytes of data in the @source_list_contents * buffer. * * @nsources_ret: On success, the length of the returned array is * returned here. * * Returns: An array of `struct wimlib_capture_source's that can be passed to * the wimlib_add_image_multisource() function to specify how a WIM image is to * be created. */ static struct wimlib_capture_source * parse_source_list(tchar **source_list_contents_p, size_t source_list_nchars, size_t *nsources_ret) { ssize_t nlines; tchar *p; struct wimlib_capture_source *sources; size_t i, j; nlines = text_file_count_lines(source_list_contents_p, &source_list_nchars); if (nlines < 0) return NULL; /* Always allocate at least 1 slot, just in case the implementation of * calloc() returns NULL if 0 bytes are requested. */ sources = calloc(nlines ?: 1, sizeof(*sources)); if (!sources) { imagex_error(T("out of memory")); return NULL; } p = *source_list_contents_p; j = 0; for (i = 0; i < nlines; i++) { /* XXX: Could use rawmemchr() here instead, but it may not be * available on all platforms. */ tchar *endp = tmemchr(p, T('\n'), source_list_nchars); size_t len = endp - p + 1; *endp = T('\0'); if (!is_comment_line(p, len)) { if (!parse_source_list_line(p, len, &sources[j++])) { free(sources); return NULL; } } p = endp + 1; } *nsources_ret = j; return sources; } /* Reads the contents of a file into memory. */ static char * file_get_contents(const tchar *filename, size_t *len_ret) { struct stat stbuf; void *buf = NULL; size_t len; FILE *fp; if (tstat(filename, &stbuf) != 0) { imagex_error_with_errno(T("Failed to stat the file \"%"TS"\""), filename); goto out; } len = stbuf.st_size; fp = tfopen(filename, T("rb")); if (!fp) { imagex_error_with_errno(T("Failed to open the file \"%"TS"\""), filename); goto out; } buf = malloc(len ? len : 1); if (!buf) { imagex_error(T("Failed to allocate buffer of %zu bytes to hold " "contents of file \"%"TS"\""), len, filename); goto out_fclose; } if (fread(buf, 1, len, fp) != len) { imagex_error_with_errno(T("Failed to read %zu bytes from the " "file \"%"TS"\""), len, filename); goto out_free_buf; } *len_ret = len; goto out_fclose; out_free_buf: free(buf); buf = NULL; out_fclose: fclose(fp); out: return buf; } /* Read standard input until EOF and return the full contents in a malloc()ed * buffer and the number of bytes of data in @len_ret. Returns NULL on read * error. */ static char * stdin_get_contents(size_t *len_ret) { /* stdin can, of course, be a pipe or other non-seekable file, so the * total length of the data cannot be pre-determined */ char *buf = NULL; size_t newlen = 1024; size_t pos = 0; size_t inc = 1024; for (;;) { char *p = realloc(buf, newlen); size_t bytes_read, bytes_to_read; if (!p) { imagex_error(T("out of memory while reading stdin")); break; } buf = p; bytes_to_read = newlen - pos; bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin); pos += bytes_read; if (bytes_read != bytes_to_read) { if (feof(stdin)) { *len_ret = pos; return buf; } else { imagex_error_with_errno(T("error reading stdin")); break; } } newlen += inc; inc *= 3; inc /= 2; } free(buf); return NULL; } static tchar * translate_text_to_tstr(char *text, size_t num_bytes, size_t *num_tchars_ret) { #ifndef __WIN32__ /* On non-Windows, assume an ASCII-compatible encoding, such as UTF-8. * */ *num_tchars_ret = num_bytes; return text; #else /* !__WIN32__ */ /* On Windows, translate the text to UTF-16LE */ wchar_t *text_wstr; size_t num_wchars; if (num_bytes >= 2 && (((unsigned char)text[0] == 0xff && (unsigned char)text[1] == 0xfe) || ((unsigned char)text[0] <= 0x7f && (unsigned char)text[1] == 0x00))) { /* File begins with 0xfeff, the BOM for UTF-16LE, or it begins * with something that looks like an ASCII character encoded as * a UTF-16LE code unit. Assume the file is encoded as * UTF-16LE. This is not a 100% reliable check. */ num_wchars = num_bytes / 2; text_wstr = (wchar_t*)text; } else { /* File does not look like UTF-16LE. Assume it is encoded in * the current Windows code page. I think these are always * ASCII-compatible, so any so-called "plain-text" (ASCII) files * should work as expected. */ text_wstr = win32_mbs_to_wcs(text, num_bytes, &num_wchars); free(text); } *num_tchars_ret = num_wchars; return text_wstr; #endif /* __WIN32__ */ } static tchar * file_get_text_contents(const tchar *filename, size_t *num_tchars_ret) { char *contents; size_t num_bytes; contents = file_get_contents(filename, &num_bytes); if (!contents) return NULL; return translate_text_to_tstr(contents, num_bytes, num_tchars_ret); } static tchar * stdin_get_text_contents(size_t *num_tchars_ret) { char *contents; size_t num_bytes; contents = stdin_get_contents(&num_bytes); if (!contents) return NULL; return translate_text_to_tstr(contents, num_bytes, num_tchars_ret); } #define TO_PERCENT(numerator, denominator) \ (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator))) #define GIBIBYTE_MIN_NBYTES 10000000000ULL #define MEBIBYTE_MIN_NBYTES 10000000ULL #define KIBIBYTE_MIN_NBYTES 10000ULL static unsigned get_unit(uint64_t total_bytes, const tchar **name_ret) { if (total_bytes >= GIBIBYTE_MIN_NBYTES) { *name_ret = T("GiB"); return 30; } else if (total_bytes >= MEBIBYTE_MIN_NBYTES) { *name_ret = T("MiB"); return 20; } else if (total_bytes >= KIBIBYTE_MIN_NBYTES) { *name_ret = T("KiB"); return 10; } else { *name_ret = T("bytes"); return 0; } } static struct wimlib_progress_info_scan last_scan_progress; static void report_scan_progress(const struct wimlib_progress_info_scan *scan, bool done) { uint64_t prev_count, cur_count; prev_count = last_scan_progress.num_nondirs_scanned + last_scan_progress.num_dirs_scanned; cur_count = scan->num_nondirs_scanned + scan->num_dirs_scanned; if (done || prev_count == 0 || cur_count >= prev_count + 100 || cur_count % 128 == 0) { unsigned unit_shift; const tchar *unit_name; unit_shift = get_unit(scan->num_bytes_scanned, &unit_name); imagex_printf(T("\r%"PRIu64" %"TS" scanned (%"PRIu64" files, " "%"PRIu64" directories) "), scan->num_bytes_scanned >> unit_shift, unit_name, scan->num_nondirs_scanned, scan->num_dirs_scanned); last_scan_progress = *scan; } } /* Progress callback function passed to various wimlib functions. */ static enum wimlib_progress_status imagex_progress_func(enum wimlib_progress_msg msg, union wimlib_progress_info *info, void *_ignored_context) { unsigned percent_done; unsigned unit_shift; const tchar *unit_name; switch (msg) { case WIMLIB_PROGRESS_MSG_WRITE_STREAMS: { static bool started; if (!started) { if (info->write_streams.compression_type != WIMLIB_COMPRESSION_TYPE_NONE) { imagex_printf(T("Using %"TS" compression " "with %u thread%"TS"\n"), wimlib_get_compression_type_string( info->write_streams.compression_type), info->write_streams.num_threads, (info->write_streams.num_threads == 1) ? T("") : T("s")); } started = true; } } unit_shift = get_unit(info->write_streams.total_bytes, &unit_name); percent_done = TO_PERCENT(info->write_streams.completed_bytes, info->write_streams.total_bytes); imagex_printf(T("\rArchiving file data: %"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"), info->write_streams.completed_bytes >> unit_shift, unit_name, info->write_streams.total_bytes >> unit_shift, unit_name, percent_done); if (info->write_streams.completed_bytes >= info->write_streams.total_bytes) imagex_printf(T("\n")); break; case WIMLIB_PROGRESS_MSG_SCAN_BEGIN: imagex_printf(T("Scanning \"%"TS"\""), info->scan.source); if (WIMLIB_IS_WIM_ROOT_PATH(info->scan.wim_target_path)) { imagex_printf(T("\n")); } else { imagex_printf(T(" (loading as WIM path: \"%"TS"\")...\n"), info->scan.wim_target_path); } memset(&last_scan_progress, 0, sizeof(last_scan_progress)); break; case WIMLIB_PROGRESS_MSG_SCAN_DENTRY: switch (info->scan.status) { case WIMLIB_SCAN_DENTRY_OK: report_scan_progress(&info->scan, false); break; case WIMLIB_SCAN_DENTRY_EXCLUDED: imagex_printf(T("\nExcluding \"%"TS"\" from capture\n"), info->scan.cur_path); break; case WIMLIB_SCAN_DENTRY_UNSUPPORTED: imagex_printf(T("\nWARNING: Excluding unsupported file or directory\n" " \"%"TS"\" from capture\n"), info->scan.cur_path); break; case WIMLIB_SCAN_DENTRY_FIXED_SYMLINK: /* Symlink fixups are enabled by default. This is * mainly intended for Windows, which for some reason * uses absolute junctions (with drive letters!) in the * default installation. On UNIX-like systems, warn the * user when fixing the target of an absolute symbolic * link, so they know to disable this if they want. */ #ifndef __WIN32__ imagex_printf(T("\nWARNING: Adjusted target of " "absolute symbolic link \"%"TS"\"\n" " (Use --norpfix to capture " "absolute symbolic links as-is)\n"), info->scan.cur_path); #endif break; default: break; } break; case WIMLIB_PROGRESS_MSG_SCAN_END: report_scan_progress(&info->scan, true); imagex_printf(T("\n")); break; case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY: unit_shift = get_unit(info->integrity.total_bytes, &unit_name); percent_done = TO_PERCENT(info->integrity.completed_bytes, info->integrity.total_bytes); imagex_printf(T("\rVerifying integrity of \"%"TS"\": %"PRIu64" %"TS" " "of %"PRIu64" %"TS" (%u%%) done"), info->integrity.filename, info->integrity.completed_bytes >> unit_shift, unit_name, info->integrity.total_bytes >> unit_shift, unit_name, percent_done); if (info->integrity.completed_bytes == info->integrity.total_bytes) imagex_printf(T("\n")); break; case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY: unit_shift = get_unit(info->integrity.total_bytes, &unit_name); percent_done = TO_PERCENT(info->integrity.completed_bytes, info->integrity.total_bytes); imagex_printf(T("\rCalculating integrity table for WIM: %"PRIu64" %"TS" " "of %"PRIu64" %"TS" (%u%%) done"), info->integrity.completed_bytes >> unit_shift, unit_name, info->integrity.total_bytes >> unit_shift, unit_name, percent_done); if (info->integrity.completed_bytes == info->integrity.total_bytes) imagex_printf(T("\n")); break; case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN: imagex_printf(T("Applying image %d (\"%"TS"\") from \"%"TS"\" " "to %"TS" \"%"TS"\"\n"), info->extract.image, info->extract.image_name, info->extract.wimfile_name, ((info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) ? T("NTFS volume") : T("directory")), info->extract.target); break; case WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE: if (info->extract.end_file_count >= 2000) { percent_done = TO_PERCENT(info->extract.current_file_count, info->extract.end_file_count); imagex_printf(T("\rCreating files: %"PRIu64" of %"PRIu64" (%u%%) done"), info->extract.current_file_count, info->extract.end_file_count, percent_done); if (info->extract.current_file_count == info->extract.end_file_count) imagex_printf(T("\n")); } break; case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS: percent_done = TO_PERCENT(info->extract.completed_bytes, info->extract.total_bytes); unit_shift = get_unit(info->extract.total_bytes, &unit_name); imagex_printf(T("\rExtracting file data: " "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"), info->extract.completed_bytes >> unit_shift, unit_name, info->extract.total_bytes >> unit_shift, unit_name, percent_done); if (info->extract.completed_bytes >= info->extract.total_bytes) imagex_printf(T("\n")); break; case WIMLIB_PROGRESS_MSG_EXTRACT_METADATA: if (info->extract.end_file_count >= 2000) { percent_done = TO_PERCENT(info->extract.current_file_count, info->extract.end_file_count); imagex_printf(T("\rApplying metadata to files: %"PRIu64" of %"PRIu64" (%u%%) done"), info->extract.current_file_count, info->extract.end_file_count, percent_done); if (info->extract.current_file_count == info->extract.end_file_count) imagex_printf(T("\n")); } break; case WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN: if (info->extract.total_parts != 1) { imagex_printf(T("\nReading split pipable WIM part %u of %u\n"), info->extract.part_number, info->extract.total_parts); } break; case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART: percent_done = TO_PERCENT(info->split.completed_bytes, info->split.total_bytes); unit_shift = get_unit(info->split.total_bytes, &unit_name); imagex_printf(T("Writing \"%"TS"\" (part %u of %u): %"PRIu64" %"TS" of " "%"PRIu64" %"TS" (%u%%) written\n"), info->split.part_name, info->split.cur_part_number, info->split.total_parts, info->split.completed_bytes >> unit_shift, unit_name, info->split.total_bytes >> unit_shift, unit_name, percent_done); break; case WIMLIB_PROGRESS_MSG_SPLIT_END_PART: if (info->split.completed_bytes == info->split.total_bytes) { imagex_printf(T("Finished writing split WIM part %u of %u\n"), info->split.cur_part_number, info->split.total_parts); } break; case WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND: switch (info->update.command->op) { case WIMLIB_UPDATE_OP_DELETE: imagex_printf(T("Deleted WIM path \"%"TS"\"\n"), info->update.command->delete_.wim_path); break; case WIMLIB_UPDATE_OP_RENAME: imagex_printf(T("Renamed WIM path \"%"TS"\" => \"%"TS"\"\n"), info->update.command->rename.wim_source_path, info->update.command->rename.wim_target_path); break; case WIMLIB_UPDATE_OP_ADD: default: break; } break; case WIMLIB_PROGRESS_MSG_REPLACE_FILE_IN_WIM: imagex_printf(T("Updating \"%"TS"\" in WIM image\n"), info->replace.path_in_wim); break; case WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE: imagex_printf(T("\nExtracting \"%"TS"\" as normal file (not WIMBoot pointer)\n"), info->wimboot_exclude.path_in_wim); break; case WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN: if (info->unmount.mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) { if (info->unmount.unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) { imagex_printf(T("Committing changes to %"TS" (image %d)\n"), info->unmount.mounted_wim, info->unmount.mounted_image); } else { imagex_printf(T("Discarding changes to %"TS" (image %d)\n"), info->unmount.mounted_wim, info->unmount.mounted_image); imagex_printf(T("\t(Use --commit to keep changes.)\n")); } } break; case WIMLIB_PROGRESS_MSG_BEGIN_VERIFY_IMAGE: imagex_printf(T("Verifying metadata for image %"PRIu32" of %"PRIu32"\n"), info->verify_image.current_image, info->verify_image.total_images); break; case WIMLIB_PROGRESS_MSG_VERIFY_STREAMS: percent_done = TO_PERCENT(info->verify_streams.completed_bytes, info->verify_streams.total_bytes); unit_shift = get_unit(info->verify_streams.total_bytes, &unit_name); imagex_printf(T("\rVerifying file data: " "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"), info->verify_streams.completed_bytes >> unit_shift, unit_name, info->verify_streams.total_bytes >> unit_shift, unit_name, percent_done); if (info->verify_streams.completed_bytes == info->verify_streams.total_bytes) imagex_printf(T("\n")); break; default: break; } imagex_flush_output(); return WIMLIB_PROGRESS_STATUS_CONTINUE; } static unsigned parse_num_threads(const tchar *optarg) { tchar *tmp; unsigned long ul_nthreads = tstrtoul(optarg, &tmp, 10); if (ul_nthreads >= UINT_MAX || *tmp || tmp == optarg) { imagex_error(T("Number of threads must be a non-negative integer!")); return UINT_MAX; } else { return ul_nthreads; } } static uint32_t parse_chunk_size(const tchar *optarg) { tchar *tmp; uint64_t chunk_size = tstrtoul(optarg, &tmp, 10); if (chunk_size == 0) { imagex_error(T("Invalid chunk size specification; must be a positive integer\n" " with optional K, M, or G suffix")); return UINT32_MAX; } if (*tmp) { if (*tmp == T('k') || *tmp == T('K')) { chunk_size <<= 10; tmp++; } else if (*tmp == T('m') || *tmp == T('M')) { chunk_size <<= 20; tmp++; } else if (*tmp == T('g') || *tmp == T('G')) { chunk_size <<= 30; tmp++; } if (*tmp && !(*tmp == T('i') && *(tmp + 1) == T('B'))) { imagex_error(T("Invalid chunk size specification; suffix must be K, M, or G")); return UINT32_MAX; } } if (chunk_size >= UINT32_MAX) { imagex_error(T("Invalid chunk size specification; the value is too large!")); return UINT32_MAX; } return chunk_size; } /* * Parse an option passed to an update command. * * @op: One of WIMLIB_UPDATE_OP_* that indicates the command being * parsed. * * @option: Text string for the option (beginning with --) * * @cmd: `struct wimlib_update_command' that is being constructed for * this command. * * Returns true if the option was recognized; false if not. */ static bool update_command_add_option(int op, const tchar *option, struct wimlib_update_command *cmd) { bool recognized = true; switch (op) { case WIMLIB_UPDATE_OP_ADD: if (!tstrcmp(option, T("--verbose"))) cmd->add.add_flags |= WIMLIB_ADD_FLAG_VERBOSE; else if (!tstrcmp(option, T("--unix-data"))) cmd->add.add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA; else if (!tstrcmp(option, T("--no-acls")) || !tstrcmp(option, T("--noacls"))) cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_ACLS; else if (!tstrcmp(option, T("--strict-acls"))) cmd->add.add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS; else if (!tstrcmp(option, T("--dereference"))) cmd->add.add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE; else if (!tstrcmp(option, T("--no-replace"))) cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE; else recognized = false; break; case WIMLIB_UPDATE_OP_DELETE: if (!tstrcmp(option, T("--force"))) cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_FORCE; else if (!tstrcmp(option, T("--recursive"))) cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE; else recognized = false; break; default: recognized = false; break; } return recognized; } /* How many nonoption arguments each `imagex update' command expects */ static const unsigned update_command_num_nonoptions[] = { [WIMLIB_UPDATE_OP_ADD] = 2, [WIMLIB_UPDATE_OP_DELETE] = 1, [WIMLIB_UPDATE_OP_RENAME] = 2, }; static void update_command_add_nonoption(int op, const tchar *nonoption, struct wimlib_update_command *cmd, unsigned num_nonoptions) { switch (op) { case WIMLIB_UPDATE_OP_ADD: if (num_nonoptions == 0) cmd->add.fs_source_path = (tchar*)nonoption; else cmd->add.wim_target_path = (tchar*)nonoption; break; case WIMLIB_UPDATE_OP_DELETE: cmd->delete_.wim_path = (tchar*)nonoption; break; case WIMLIB_UPDATE_OP_RENAME: if (num_nonoptions == 0) cmd->rename.wim_source_path = (tchar*)nonoption; else cmd->rename.wim_target_path = (tchar*)nonoption; break; } } /* * Parse a command passed on stdin to `imagex update'. * * @line: Text of the command. * @len: Length of the line, including a null terminator * at line[len - 1]. * * @command: A `struct wimlib_update_command' to fill in from the parsed * line. * * @line_number: Line number of the command, for diagnostics. * * Returns true on success; returns false on parse error. */ static bool parse_update_command(tchar *line, size_t len, struct wimlib_update_command *command, size_t line_number) { int ret; tchar *command_name; int op; size_t num_nonoptions; /* Get the command name ("add", "delete", "rename") */ ret = parse_string(&line, &len, &command_name); if (ret != PARSE_STRING_SUCCESS) return false; if (!tstrcasecmp(command_name, T("add"))) { op = WIMLIB_UPDATE_OP_ADD; } else if (!tstrcasecmp(command_name, T("delete"))) { op = WIMLIB_UPDATE_OP_DELETE; } else if (!tstrcasecmp(command_name, T("rename"))) { op = WIMLIB_UPDATE_OP_RENAME; } else { imagex_error(T("Unknown update command \"%"TS"\" on line %zu"), command_name, line_number); return false; } command->op = op; /* Parse additional options and non-options as needed */ num_nonoptions = 0; for (;;) { tchar *next_string; ret = parse_string(&line, &len, &next_string); if (ret == PARSE_STRING_NONE) /* End of line */ break; else if (ret != PARSE_STRING_SUCCESS) /* Parse failure */ return false; if (next_string[0] == T('-') && next_string[1] == T('-')) { /* Option */ if (!update_command_add_option(op, next_string, command)) { imagex_error(T("Unrecognized option \"%"TS"\" to " "update command \"%"TS"\" on line %zu"), next_string, command_name, line_number); return false; } } else { /* Nonoption */ if (num_nonoptions == update_command_num_nonoptions[op]) { imagex_error(T("Unexpected argument \"%"TS"\" in " "update command on line %zu\n" " (The \"%"TS"\" command only " "takes %zu nonoption arguments!)\n"), next_string, line_number, command_name, num_nonoptions); return false; } update_command_add_nonoption(op, next_string, command, num_nonoptions); num_nonoptions++; } } if (num_nonoptions != update_command_num_nonoptions[op]) { imagex_error(T("Not enough arguments to update command " "\"%"TS"\" on line %zu"), command_name, line_number); return false; } return true; } static struct wimlib_update_command * parse_update_command_file(tchar **cmd_file_contents_p, size_t cmd_file_nchars, size_t *num_cmds_ret) { ssize_t nlines; tchar *p; struct wimlib_update_command *cmds; size_t i, j; nlines = text_file_count_lines(cmd_file_contents_p, &cmd_file_nchars); if (nlines < 0) return NULL; /* Always allocate at least 1 slot, just in case the implementation of * calloc() returns NULL if 0 bytes are requested. */ cmds = calloc(nlines ?: 1, sizeof(struct wimlib_update_command)); if (!cmds) { imagex_error(T("out of memory")); return NULL; } p = *cmd_file_contents_p; j = 0; for (i = 0; i < nlines; i++) { /* XXX: Could use rawmemchr() here instead, but it may not be * available on all platforms. */ tchar *endp = tmemchr(p, T('\n'), cmd_file_nchars); size_t len = endp - p + 1; *endp = T('\0'); if (!is_comment_line(p, len)) { if (!parse_update_command(p, len, &cmds[j++], i + 1)) { free(cmds); return NULL; } } p = endp + 1; } *num_cmds_ret = j; return cmds; } /* Apply one image, or all images, from a WIM file to a directory, OR apply * one image from a WIM file to an NTFS volume. */ static int imagex_apply(int argc, tchar **argv, int cmd) { int c; int open_flags = 0; int image = WIMLIB_NO_IMAGE; WIMStruct *wim; struct wimlib_wim_info info; int ret; const tchar *wimfile; const tchar *target; const tchar *image_num_or_name = NULL; int extract_flags = 0; STRING_LIST(refglobs); for_opt(c, apply_options) { switch (c) { case IMAGEX_CHECK_OPTION: open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; break; case IMAGEX_VERBOSE_OPTION: /* No longer does anything. */ break; case IMAGEX_REF_OPTION: ret = string_list_append(&refglobs, optarg); if (ret) goto out_free_refglobs; break; case IMAGEX_UNIX_DATA_OPTION: extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA; break; case IMAGEX_NO_ACLS_OPTION: extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS; break; case IMAGEX_STRICT_ACLS_OPTION: extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS; break; case IMAGEX_NO_ATTRIBUTES_OPTION: extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES; break; case IMAGEX_NORPFIX_OPTION: extract_flags |= WIMLIB_EXTRACT_FLAG_NORPFIX; break; case IMAGEX_RPFIX_OPTION: extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX; break; case IMAGEX_INCLUDE_INVALID_NAMES_OPTION: extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES; extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS; break; case IMAGEX_WIMBOOT_OPTION: extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT; break; case IMAGEX_COMPACT_OPTION: ret = set_compact_mode(optarg, &extract_flags); if (ret) goto out_free_refglobs; break; default: goto out_usage; } } argc -= optind; argv += optind; if (argc != 2 && argc != 3) goto out_usage; wimfile = argv[0]; if (!tstrcmp(wimfile, T("-"))) { /* Attempt to apply pipable WIM from standard input. */ if (argc == 2) { image_num_or_name = NULL; target = argv[1]; } else { image_num_or_name = argv[1]; target = argv[2]; } wim = NULL; } else { ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim, imagex_progress_func, NULL); if (ret) goto out_free_refglobs; wimlib_get_wim_info(wim, &info); if (argc >= 3) { /* Image explicitly specified. */ image_num_or_name = argv[1]; image = wimlib_resolve_image(wim, image_num_or_name); ret = verify_image_exists(image, image_num_or_name, wimfile); if (ret) goto out_wimlib_free; target = argv[2]; } else { /* No image specified; default to image 1, but only if the WIM * contains exactly one image. */ if (info.image_count != 1) { imagex_error(T("\"%"TS"\" contains %d images; " "Please select one (or all)."), wimfile, info.image_count); wimlib_free(wim); goto out_usage; } image = 1; target = argv[1]; } } if (refglobs.num_strings) { if (wim == NULL) { imagex_error(T("Can't specify --ref when applying from stdin!")); ret = -1; goto out_wimlib_free; } ret = wim_reference_globs(wim, &refglobs, open_flags); if (ret) goto out_wimlib_free; } #ifndef __WIN32__ { /* Interpret a regular file or block device target as an NTFS * volume. */ struct stat stbuf; if (tstat(target, &stbuf)) { if (errno != ENOENT) { imagex_error_with_errno(T("Failed to stat \"%"TS"\""), target); ret = -1; goto out_wimlib_free; } } else { if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS; } } #endif if (wim) { ret = wimlib_extract_image(wim, image, target, extract_flags); } else { set_fd_to_binary_mode(STDIN_FILENO); ret = wimlib_extract_image_from_pipe_with_progress( STDIN_FILENO, image_num_or_name, target, extract_flags, imagex_progress_func, NULL); } if (ret == 0) { imagex_printf(T("Done applying WIM image.\n")); } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) { if (wim) { do_resource_not_found_warning(wimfile, &info, &refglobs); } else { imagex_error(T( "If you are applying an image " "from a split pipable WIM,\n" " make sure you have " "concatenated together all parts.")); } } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND && wim) { do_metadata_not_found_warning(wimfile, &info); } out_wimlib_free: wimlib_free(wim); out_free_refglobs: string_list_destroy(&refglobs); return ret; out_usage: usage(CMD_APPLY, stderr); ret = -1; goto out_free_refglobs; } /* * Create a WIM image from a directory tree, NTFS volume, or multiple files or * directory trees. 'wimcapture': create a new WIM file containing the desired * image. 'wimappend': add a new image to an existing WIM file; or, with * '--create' behave like 'wimcapture' if the WIM file doesn't exist. */ static int imagex_capture_or_append(int argc, tchar **argv, int cmd) { int c; bool create = false; bool appending = (cmd == CMD_APPEND); int open_flags = 0; int add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE | WIMLIB_ADD_FLAG_WINCONFIG | WIMLIB_ADD_FLAG_VERBOSE | WIMLIB_ADD_FLAG_FILE_PATHS_UNNEEDED; int write_flags = 0; int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID; uint32_t chunk_size = UINT32_MAX; uint32_t solid_chunk_size = UINT32_MAX; int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID; const tchar *wimfile; int wim_fd; const tchar *name; STRING_LIST(image_properties); WIMStruct *wim; STRING_LIST(base_wimfiles); WIMStruct **base_wims; WIMStruct *template_wim = NULL; const tchar *template_wimfile = NULL; const tchar *template_image_name_or_num = NULL; int template_image = WIMLIB_NO_IMAGE; int ret; unsigned num_threads = 0; tchar *source; tchar *source_copy; tchar *config_file = NULL; bool source_list = false; size_t source_list_nchars = 0; tchar *source_list_contents; bool capture_sources_malloced; struct wimlib_capture_source *capture_sources; size_t num_sources; bool name_defaulted; for_opt(c, capture_or_append_options) { switch (c) { case IMAGEX_BOOT_OPTION: add_flags |= WIMLIB_ADD_FLAG_BOOT; break; case IMAGEX_CHECK_OPTION: open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; /* fall-through */ case IMAGEX_INCLUDE_INTEGRITY_OPTION: write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; break; case IMAGEX_NOCHECK_OPTION: write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY; break; case IMAGEX_CONFIG_OPTION: config_file = optarg; add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG; break; case IMAGEX_COMPRESS_OPTION: compression_type = get_compression_type(optarg, false); if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) goto out_err; break; case IMAGEX_CHUNK_SIZE_OPTION: chunk_size = parse_chunk_size(optarg); if (chunk_size == UINT32_MAX) goto out_err; break; case IMAGEX_SOLID_CHUNK_SIZE_OPTION: solid_chunk_size = parse_chunk_size(optarg); if (solid_chunk_size == UINT32_MAX) goto out_err; break; case IMAGEX_SOLID_COMPRESS_OPTION: solid_ctype = get_compression_type(optarg, true); if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID) goto out_err; break; case IMAGEX_SOLID_OPTION: write_flags |= WIMLIB_WRITE_FLAG_SOLID; break; case IMAGEX_NO_SOLID_SORT_OPTION: write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT; break; case IMAGEX_FLAGS_OPTION: { tchar *p = alloca((6 + tstrlen(optarg) + 1) * sizeof(tchar)); tsprintf(p, T("FLAGS=%"TS), optarg); ret = string_list_append(&image_properties, p); if (ret) goto out; break; } case IMAGEX_IMAGE_PROPERTY_OPTION: ret = append_image_property_argument(&image_properties); if (ret) goto out; break; case IMAGEX_DEREFERENCE_OPTION: add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE; break; case IMAGEX_VERBOSE_OPTION: /* No longer does anything. */ break; case IMAGEX_THREADS_OPTION: num_threads = parse_num_threads(optarg); if (num_threads == UINT_MAX) goto out_err; break; case IMAGEX_REBUILD_OPTION: write_flags |= WIMLIB_WRITE_FLAG_REBUILD; break; case IMAGEX_UNIX_DATA_OPTION: add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA; break; case IMAGEX_SOURCE_LIST_OPTION: source_list = true; break; case IMAGEX_NO_ACLS_OPTION: add_flags |= WIMLIB_ADD_FLAG_NO_ACLS; break; case IMAGEX_STRICT_ACLS_OPTION: add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS; break; case IMAGEX_RPFIX_OPTION: add_flags |= WIMLIB_ADD_FLAG_RPFIX; break; case IMAGEX_NORPFIX_OPTION: add_flags |= WIMLIB_ADD_FLAG_NORPFIX; break; case IMAGEX_PIPABLE_OPTION: write_flags |= WIMLIB_WRITE_FLAG_PIPABLE; break; case IMAGEX_NOT_PIPABLE_OPTION: write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE; break; case IMAGEX_UPDATE_OF_OPTION: if (template_image_name_or_num) { imagex_error(T("'--update-of' can only be " "specified one time!")); goto out_err; } else { tchar *colon; colon = tstrrchr(optarg, T(':')); if (colon) { template_wimfile = optarg; *colon = T('\0'); template_image_name_or_num = colon + 1; } else { template_wimfile = NULL; template_image_name_or_num = optarg; } } #ifdef __WIN32__ imagex_printf(T("[WARNING] '--update-of' is unreliable on Windows!\n")); #endif break; case IMAGEX_DELTA_FROM_OPTION: ret = string_list_append(&base_wimfiles, optarg); if (ret) goto out; write_flags |= WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS; break; case IMAGEX_WIMBOOT_OPTION: add_flags |= WIMLIB_ADD_FLAG_WIMBOOT; break; case IMAGEX_UNSAFE_COMPACT_OPTION: write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT; break; case IMAGEX_SNAPSHOT_OPTION: add_flags |= WIMLIB_ADD_FLAG_SNAPSHOT; break; case IMAGEX_CREATE_OPTION: if (cmd == CMD_CAPTURE) { imagex_error(T("'--create' is only valid for 'wimappend', not 'wimcapture'")); goto out_err; } create = true; break; default: goto out_usage; } } argc -= optind; argv += optind; if (argc < 2 || argc > 4) goto out_usage; source = argv[0]; wimfile = argv[1]; /* Set default compression type and parameters. */ if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) { /* No compression type specified. Use the default. */ if (add_flags & WIMLIB_ADD_FLAG_WIMBOOT) { /* With --wimboot, default to XPRESS compression. */ compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS; } else if (write_flags & WIMLIB_WRITE_FLAG_SOLID) { /* With --solid, default to LZMS compression. (However, * this will not affect solid resources!) */ compression_type = WIMLIB_COMPRESSION_TYPE_LZMS; } else { /* Otherwise, default to LZX compression. */ compression_type = WIMLIB_COMPRESSION_TYPE_LZX; } } if (!tstrcmp(wimfile, T("-"))) { /* Writing captured WIM to standard output. */ if (create) appending = false; #if 0 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) { imagex_error("Can't write a non-pipable WIM to " "standard output! Specify --pipable\n" " if you want to create a pipable WIM " "(but read the docs first)."); goto out_err; } #else write_flags |= WIMLIB_WRITE_FLAG_PIPABLE; #endif if (appending) { imagex_error(T("Using standard output for append does " "not make sense.")); goto out_err; } wim_fd = STDOUT_FILENO; wimfile = NULL; imagex_output_to_stderr(); set_fd_to_binary_mode(wim_fd); } else { struct stat stbuf; /* Check for 'wimappend --create' acting as wimcapture */ if (create && tstat(wimfile, &stbuf) != 0 && errno == ENOENT) { appending = false; /* Ignore '--update-of' for the target WIMFILE */ if (template_image_name_or_num && (!template_wimfile || !tstrcmp(template_wimfile, wimfile))) { template_image_name_or_num = NULL; template_wimfile = NULL; } } } if ((write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) && !appending) { imagex_error(T("'--unsafe-compact' is only valid for append!")); goto out_err; } /* If template image was specified using --update-of=IMAGE rather * than --update-of=WIMFILE:IMAGE, set the default WIMFILE. */ if (template_image_name_or_num && !template_wimfile) { if (base_wimfiles.num_strings == 1) { /* Capturing delta WIM based on single WIM: default to * base WIM. */ template_wimfile = base_wimfiles.strings[0]; } else if (appending) { /* Appending to WIM: default to WIM being appended to. */ template_wimfile = wimfile; } else { /* Capturing a normal (non-delta) WIM, so the WIM file * *must* be explicitly specified. */ if (base_wimfiles.num_strings > 1) { imagex_error(T("For capture of delta WIM " "based on multiple existing " "WIMs,\n" " '--update-of' must " "specify WIMFILE:IMAGE!")); } else { imagex_error(T("For capture of non-delta WIM, " "'--update-of' must specify " "WIMFILE:IMAGE!")); } goto out_usage; } } if (argc >= 3) { name = argv[2]; name_defaulted = false; } else { /* Set default name to SOURCE argument, omitting any directory * prefixes and trailing slashes. This requires making a copy * of @source. Leave some free characters at the end in case we * append a number to keep the name unique. */ size_t source_name_len; source_name_len = tstrlen(source); source_copy = alloca((source_name_len + 1 + 25) * sizeof(tchar)); name = tbasename(tstrcpy(source_copy, source)); name_defaulted = true; } /* Image description (if given). */ if (argc >= 4) { tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar)); tsprintf(p, T("DESCRIPTION=%"TS), argv[3]); ret = string_list_append(&image_properties, p); if (ret) goto out; } if (source_list) { /* Set up capture sources in source list mode */ if (source[0] == T('-') && source[1] == T('\0')) { source_list_contents = stdin_get_text_contents(&source_list_nchars); } else { source_list_contents = file_get_text_contents(source, &source_list_nchars); } if (!source_list_contents) goto out_err; capture_sources = parse_source_list(&source_list_contents, source_list_nchars, &num_sources); if (!capture_sources) { ret = -1; goto out_free_source_list_contents; } capture_sources_malloced = true; } else { /* Set up capture source in non-source-list mode. */ capture_sources = alloca(sizeof(struct wimlib_capture_source)); capture_sources[0].fs_source_path = source; capture_sources[0].wim_target_path = WIMLIB_WIM_ROOT_PATH; capture_sources[0].reserved = 0; num_sources = 1; capture_sources_malloced = false; source_list_contents = NULL; } /* Open the existing WIM, or create a new one. */ if (appending) { ret = wimlib_open_wim_with_progress(wimfile, open_flags | WIMLIB_OPEN_FLAG_WRITE_ACCESS, &wim, imagex_progress_func, NULL); if (ret) goto out_free_capture_sources; } else { ret = wimlib_create_new_wim(compression_type, &wim); if (ret) goto out_free_capture_sources; wimlib_register_progress_function(wim, imagex_progress_func, NULL); } /* Set chunk size if non-default. */ if (chunk_size != UINT32_MAX) { ret = wimlib_set_output_chunk_size(wim, chunk_size); if (ret) goto out_free_wim; } else if ((add_flags & WIMLIB_ADD_FLAG_WIMBOOT)) { int ctype = compression_type; if (appending) { struct wimlib_wim_info info; wimlib_get_wim_info(wim, &info); ctype = info.compression_type; } if (ctype == WIMLIB_COMPRESSION_TYPE_XPRESS) { ret = wimlib_set_output_chunk_size(wim, 4096); if (ret) goto out_free_wim; } } if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) { ret = wimlib_set_output_pack_compression_type(wim, solid_ctype); if (ret) goto out_free_wim; } if (solid_chunk_size != UINT32_MAX) { ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size); if (ret) goto out_free_wim; } #ifndef __WIN32__ /* Detect if source is regular file or block device and set NTFS volume * capture mode. */ if (!source_list) { struct stat stbuf; if (tstat(source, &stbuf) == 0) { if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) { imagex_printf(T("Capturing WIM image from NTFS " "filesystem on \"%"TS"\"\n"), source); add_flags |= WIMLIB_ADD_FLAG_NTFS; } } else { if (errno != ENOENT) { imagex_error_with_errno(T("Failed to stat " "\"%"TS"\""), source); ret = -1; goto out_free_wim; } } } #endif /* If the user did not specify an image name, and the basename of the * source already exists as an image name in the WIM file, append a * suffix to make it unique. */ if (appending && name_defaulted) { unsigned long conflict_idx; tchar *name_end = tstrchr(name, T('\0')); for (conflict_idx = 1; wimlib_image_name_in_use(wim, name); conflict_idx++) { tsprintf(name_end, T(" (%lu)"), conflict_idx); } } /* If capturing a delta WIM, reference resources from the base WIMs * before adding the new image. */ if (base_wimfiles.num_strings) { base_wims = calloc(base_wimfiles.num_strings, sizeof(base_wims[0])); if (base_wims == NULL) { imagex_error(T("Out of memory!")); ret = -1; goto out_free_wim; } for (size_t i = 0; i < base_wimfiles.num_strings; i++) { ret = wimlib_open_wim_with_progress( base_wimfiles.strings[i], open_flags, &base_wims[i], imagex_progress_func, NULL); if (ret) goto out_free_base_wims; } ret = wimlib_reference_resources(wim, base_wims, base_wimfiles.num_strings, 0); if (ret) goto out_free_base_wims; if (base_wimfiles.num_strings == 1) { imagex_printf(T("Capturing delta WIM based on \"%"TS"\"\n"), base_wimfiles.strings[0]); } else { imagex_printf(T("Capturing delta WIM based on %u WIMs\n"), base_wimfiles.num_strings); } } else { base_wims = NULL; } /* If capturing or appending as an update of an existing (template) image, * open the WIM if needed and parse the image index. */ if (template_image_name_or_num) { if (appending && !tstrcmp(template_wimfile, wimfile)) { template_wim = wim; } else { for (size_t i = 0; i < base_wimfiles.num_strings; i++) { if (!tstrcmp(template_wimfile, base_wimfiles.strings[i])) { template_wim = base_wims[i]; break; } } } if (!template_wim) { ret = wimlib_open_wim_with_progress(template_wimfile, open_flags, &template_wim, imagex_progress_func, NULL); if (ret) goto out_free_base_wims; } template_image = wimlib_resolve_image(template_wim, template_image_name_or_num); if (template_image_name_or_num[0] == T('-')) { tchar *tmp; unsigned long n; struct wimlib_wim_info info; wimlib_get_wim_info(template_wim, &info); n = tstrtoul(template_image_name_or_num + 1, &tmp, 10); if (n >= 1 && n <= info.image_count && *tmp == T('\0') && tmp != template_image_name_or_num + 1) { template_image = info.image_count - (n - 1); } } ret = verify_image_exists_and_is_single(template_image, template_image_name_or_num, template_wimfile); if (ret) goto out_free_template_wim; } ret = wimlib_add_image_multisource(wim, capture_sources, num_sources, name, config_file, add_flags); if (ret) goto out_free_template_wim; if (image_properties.num_strings || template_image_name_or_num) { /* User asked to set additional image properties, or an image on * which the added one is to be based has been specified with * --update-of. */ struct wimlib_wim_info info; wimlib_get_wim_info(wim, &info); ret = apply_image_properties(&image_properties, wim, info.image_count, NULL); if (ret) goto out_free_template_wim; /* Reference template image if the user provided one. */ if (template_image_name_or_num) { imagex_printf(T("Using image %d " "from \"%"TS"\" as template\n"), template_image, template_wimfile); ret = wimlib_reference_template_image(wim, info.image_count, template_wim, template_image, 0); if (ret) goto out_free_template_wim; } } /* Write the new WIM or overwrite the existing WIM with the new image * appended. */ if (appending) { ret = wimlib_overwrite(wim, write_flags, num_threads); } else if (wimfile) { ret = wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES, write_flags, num_threads); } else { ret = wimlib_write_to_fd(wim, wim_fd, WIMLIB_ALL_IMAGES, write_flags, num_threads); } out_free_template_wim: /* 'template_wim' may alias 'wim' or any of the 'base_wims' */ if (template_wim == wim) goto out_free_base_wims; for (size_t i = 0; i < base_wimfiles.num_strings; i++) if (template_wim == base_wims[i]) goto out_free_base_wims; wimlib_free(template_wim); out_free_base_wims: for (size_t i = 0; i < base_wimfiles.num_strings; i++) wimlib_free(base_wims[i]); free(base_wims); out_free_wim: wimlib_free(wim); out_free_capture_sources: if (capture_sources_malloced) free(capture_sources); out_free_source_list_contents: free(source_list_contents); out: string_list_destroy(&image_properties); string_list_destroy(&base_wimfiles); return ret; out_usage: usage(cmd, stderr); out_err: ret = -1; goto out; } /* Remove image(s) from a WIM. */ static int imagex_delete(int argc, tchar **argv, int cmd) { int c; int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS; int write_flags = 0; const tchar *wimfile; const tchar *image_num_or_name; WIMStruct *wim; int image; int ret; for_opt(c, delete_options) { switch (c) { case IMAGEX_CHECK_OPTION: open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; /* fall-through */ case IMAGEX_INCLUDE_INTEGRITY_OPTION: write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; break; case IMAGEX_SOFT_OPTION: write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE; break; case IMAGEX_UNSAFE_COMPACT_OPTION: write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT; break; default: goto out_usage; } } argc -= optind; argv += optind; if (argc != 2) { if (argc < 1) imagex_error(T("Must specify a WIM file")); if (argc < 2) imagex_error(T("Must specify an image")); goto out_usage; } wimfile = argv[0]; image_num_or_name = argv[1]; ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim, imagex_progress_func, NULL); if (ret) goto out; image = wimlib_resolve_image(wim, image_num_or_name); ret = verify_image_exists(image, image_num_or_name, wimfile); if (ret) goto out_wimlib_free; ret = wimlib_delete_image(wim, image); if (ret) { imagex_error(T("Failed to delete image from \"%"TS"\""), wimfile); goto out_wimlib_free; } ret = wimlib_overwrite(wim, write_flags, 0); if (ret) { imagex_error(T("Failed to write the file \"%"TS"\" with image " "deleted"), wimfile); } out_wimlib_free: wimlib_free(wim); out: return ret; out_usage: usage(CMD_DELETE, stderr); ret = -1; goto out; } struct print_dentry_options { bool detailed; }; static void print_dentry_full_path(const struct wimlib_dir_entry *dentry) { tprintf(T("%"TS"\n"), dentry->full_path); } static const struct { uint32_t flag; const tchar *name; } file_attr_flags[] = { {WIMLIB_FILE_ATTRIBUTE_READONLY, T("READONLY")}, {WIMLIB_FILE_ATTRIBUTE_HIDDEN, T("HIDDEN")}, {WIMLIB_FILE_ATTRIBUTE_SYSTEM, T("SYSTEM")}, {WIMLIB_FILE_ATTRIBUTE_DIRECTORY, T("DIRECTORY")}, {WIMLIB_FILE_ATTRIBUTE_ARCHIVE, T("ARCHIVE")}, {WIMLIB_FILE_ATTRIBUTE_DEVICE, T("DEVICE")}, {WIMLIB_FILE_ATTRIBUTE_NORMAL, T("NORMAL")}, {WIMLIB_FILE_ATTRIBUTE_TEMPORARY, T("TEMPORARY")}, {WIMLIB_FILE_ATTRIBUTE_SPARSE_FILE, T("SPARSE_FILE")}, {WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT, T("REPARSE_POINT")}, {WIMLIB_FILE_ATTRIBUTE_COMPRESSED, T("COMPRESSED")}, {WIMLIB_FILE_ATTRIBUTE_OFFLINE, T("OFFLINE")}, {WIMLIB_FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, T("NOT_CONTENT_INDEXED")}, {WIMLIB_FILE_ATTRIBUTE_ENCRYPTED, T("ENCRYPTED")}, {WIMLIB_FILE_ATTRIBUTE_VIRTUAL, T("VIRTUAL")}, }; #define TIMESTR_MAX 100 static void print_time(const tchar *type, const struct wimlib_timespec *wts, int32_t high_part) { tchar timestr[TIMESTR_MAX]; time_t t; struct tm tm; if (sizeof(wts->tv_sec) == 4 && sizeof(t) > sizeof(wts->tv_sec)) t = (uint32_t)wts->tv_sec | ((uint64_t)high_part << 32); else t = wts->tv_sec; gmtime_r(&t, &tm); tstrftime(timestr, TIMESTR_MAX, T("%a %b %d %H:%M:%S %Y UTC"), &tm); timestr[TIMESTR_MAX - 1] = '\0'; tprintf(T("%-20"TS"= %"TS"\n"), type, timestr); } static void print_byte_field(const uint8_t field[], size_t len) { while (len--) tprintf(T("%02hhx"), *field++); } static void print_wim_information(const tchar *wimfile, const struct wimlib_wim_info *info) { tchar attr_string[256]; tchar *p; tputs(T("WIM Information:")); tputs(T("----------------")); tprintf(T("Path: %"TS"\n"), wimfile); tprintf(T("GUID: 0x")); print_byte_field(info->guid, sizeof(info->guid)); tputchar(T('\n')); tprintf(T("Version: %u\n"), info->wim_version); tprintf(T("Image Count: %d\n"), info->image_count); tprintf(T("Compression: %"TS"\n"), wimlib_get_compression_type_string(info->compression_type)); tprintf(T("Chunk Size: %"PRIu32" bytes\n"), info->chunk_size); tprintf(T("Part Number: %d/%d\n"), info->part_number, info->total_parts); tprintf(T("Boot Index: %d\n"), info->boot_index); tprintf(T("Size: %"PRIu64" bytes\n"), info->total_bytes); attr_string[0] = T('\0'); if (info->pipable) tstrcat(attr_string, T("Pipable, ")); if (info->has_integrity_table) tstrcat(attr_string, T("Integrity info, ")); if (info->has_rpfix) tstrcat(attr_string, T("Relative path junction, ")); if (info->resource_only) tstrcat(attr_string, T("Resource only, ")); if (info->metadata_only) tstrcat(attr_string, T("Metadata only, ")); if (info->is_marked_readonly) tstrcat(attr_string, T("Readonly, ")); p = tstrchr(attr_string, T('\0')); if (p >= &attr_string[2] && p[-1] == T(' ') && p[-2] == T(',')) p[-2] = T('\0'); tprintf(T("Attributes: %"TS"\n\n"), attr_string); } static int print_resource(const struct wimlib_resource_entry *resource, void *_ignore) { tprintf(T("Hash = 0x")); print_byte_field(resource->sha1_hash, sizeof(resource->sha1_hash)); tputchar(T('\n')); if (!resource->is_missing) { tprintf(T("Uncompressed size = %"PRIu64" bytes\n"), resource->uncompressed_size); if (resource->packed) { tprintf(T("Solid resource = %"PRIu64" => %"PRIu64" " "bytes @ offset %"PRIu64"\n"), resource->raw_resource_uncompressed_size, resource->raw_resource_compressed_size, resource->raw_resource_offset_in_wim); tprintf(T("Solid offset = %"PRIu64" bytes\n"), resource->offset); } else { tprintf(T("Compressed size = %"PRIu64" bytes\n"), resource->compressed_size); tprintf(T("Offset in WIM = %"PRIu64" bytes\n"), resource->offset); } tprintf(T("Part Number = %u\n"), resource->part_number); tprintf(T("Reference Count = %u\n"), resource->reference_count); tprintf(T("Flags = ")); if (resource->is_compressed) tprintf(T("WIM_RESHDR_FLAG_COMPRESSED ")); if (resource->is_metadata) tprintf(T("WIM_RESHDR_FLAG_METADATA ")); if (resource->is_free) tprintf(T("WIM_RESHDR_FLAG_FREE ")); if (resource->is_spanned) tprintf(T("WIM_RESHDR_FLAG_SPANNED ")); if (resource->packed) tprintf(T("WIM_RESHDR_FLAG_SOLID ")); tputchar(T('\n')); } tputchar(T('\n')); return 0; } static void print_blobs(WIMStruct *wim) { wimlib_iterate_lookup_table(wim, 0, print_resource, NULL); } #ifndef __WIN32__ static void default_print_security_descriptor(const uint8_t *sd, size_t size) { tprintf(T("Security Descriptor = ")); print_byte_field(sd, size); tputchar(T('\n')); } #endif static bool is_null_guid(const uint8_t *guid) { static const uint8_t null_guid[WIMLIB_GUID_LEN]; return !memcmp(guid, null_guid, WIMLIB_GUID_LEN); } static void print_guid(const tchar *label, const uint8_t *guid) { if (is_null_guid(guid)) return; tprintf(T("%-20"TS"= 0x"), label); print_byte_field(guid, WIMLIB_GUID_LEN); tputchar(T('\n')); } static void print_dentry_detailed(const struct wimlib_dir_entry *dentry) { tprintf(T( "----------------------------------------------------------------------------\n")); tprintf(T("Full Path = \"%"TS"\"\n"), dentry->full_path); if (dentry->dos_name) tprintf(T("Short Name = \"%"TS"\"\n"), dentry->dos_name); tprintf(T("Attributes = 0x%08x\n"), dentry->attributes); for (size_t i = 0; i < ARRAY_LEN(file_attr_flags); i++) if (file_attr_flags[i].flag & dentry->attributes) tprintf(T(" FILE_ATTRIBUTE_%"TS" is set\n"), file_attr_flags[i].name); if (dentry->security_descriptor) { print_security_descriptor(dentry->security_descriptor, dentry->security_descriptor_size); } print_time(T("Creation Time"), &dentry->creation_time, dentry->creation_time_high); print_time(T("Last Write Time"), &dentry->last_write_time, dentry->last_write_time_high); print_time(T("Last Access Time"), &dentry->last_access_time, dentry->last_access_time_high); if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT) tprintf(T("Reparse Tag = 0x%"PRIx32"\n"), dentry->reparse_tag); tprintf(T("Link Group ID = 0x%016"PRIx64"\n"), dentry->hard_link_group_id); tprintf(T("Link Count = %"PRIu32"\n"), dentry->num_links); if (dentry->unix_mode != 0) { tprintf(T("UNIX Data = uid:%"PRIu32" gid:%"PRIu32" " "mode:0%"PRIo32" rdev:0x%"PRIx32"\n"), dentry->unix_uid, dentry->unix_gid, dentry->unix_mode, dentry->unix_rdev); } if (!is_null_guid(dentry->object_id.object_id)) { print_guid(T("Object ID"), dentry->object_id.object_id); print_guid(T("Birth Volume ID"), dentry->object_id.birth_volume_id); print_guid(T("Birth Object ID"), dentry->object_id.birth_object_id); print_guid(T("Domain ID"), dentry->object_id.domain_id); } for (uint32_t i = 0; i <= dentry->num_named_streams; i++) { if (dentry->streams[i].stream_name) { tprintf(T("\tNamed data stream \"%"TS"\":\n"), dentry->streams[i].stream_name); } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_ENCRYPTED) { tprintf(T("\tRaw encrypted data stream:\n")); } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT) { tprintf(T("\tReparse point stream:\n")); } else { tprintf(T("\tUnnamed data stream:\n")); } print_resource(&dentry->streams[i].resource, NULL); } } static int print_dentry(const struct wimlib_dir_entry *dentry, void *_options) { const struct print_dentry_options *options = _options; if (!options->detailed) print_dentry_full_path(dentry); else print_dentry_detailed(dentry); return 0; } /* Print the files contained in an image(s) in a WIM file. */ static int imagex_dir(int argc, tchar **argv, int cmd) { const tchar *wimfile; WIMStruct *wim = NULL; int image; int ret; const tchar *path = WIMLIB_WIM_ROOT_PATH; int c; struct print_dentry_options options = { .detailed = false, }; int iterate_flags = WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE; STRING_LIST(refglobs); for_opt(c, dir_options) { switch (c) { case IMAGEX_PATH_OPTION: path = optarg; break; case IMAGEX_DETAILED_OPTION: options.detailed = true; break; case IMAGEX_ONE_FILE_ONLY_OPTION: iterate_flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE; break; case IMAGEX_REF_OPTION: ret = string_list_append(&refglobs, optarg); if (ret) goto out_free_refglobs; break; default: goto out_usage; } } argc -= optind; argv += optind; if (argc < 1) { imagex_error(T("Must specify a WIM file")); goto out_usage; } if (argc > 2) { imagex_error(T("Too many arguments")); goto out_usage; } wimfile = argv[0]; ret = wimlib_open_wim_with_progress(wimfile, 0, &wim, imagex_progress_func, NULL); if (ret) goto out_free_refglobs; if (argc >= 2) { image = wimlib_resolve_image(wim, argv[1]); ret = verify_image_exists(image, argv[1], wimfile); if (ret) goto out_wimlib_free; } else { /* No image specified; default to image 1, but only if the WIM * contains exactly one image. */ struct wimlib_wim_info info; wimlib_get_wim_info(wim, &info); if (info.image_count != 1) { imagex_error(T("\"%"TS"\" contains %d images; Please " "select one (or all)."), wimfile, info.image_count); wimlib_free(wim); goto out_usage; } image = 1; } if (refglobs.num_strings) { ret = wim_reference_globs(wim, &refglobs, 0); if (ret) goto out_wimlib_free; } ret = wimlib_iterate_dir_tree(wim, image, path, iterate_flags, print_dentry, &options); if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) { struct wimlib_wim_info info; wimlib_get_wim_info(wim, &info); do_metadata_not_found_warning(wimfile, &info); } out_wimlib_free: wimlib_free(wim); out_free_refglobs: string_list_destroy(&refglobs); return ret; out_usage: usage(CMD_DIR, stderr); ret = -1; goto out_free_refglobs; } /* Exports one, or all, images from a WIM file to a new WIM file or an existing * WIM file. */ static int imagex_export(int argc, tchar **argv, int cmd) { int c; int open_flags = 0; int export_flags = WIMLIB_EXPORT_FLAG_GIFT; int write_flags = 0; int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID; const tchar *src_wimfile; const tchar *src_image_num_or_name; const tchar *dest_wimfile; int dest_wim_fd; const tchar *dest_name; const tchar *dest_desc; WIMStruct *src_wim; struct wimlib_wim_info src_info; WIMStruct *dest_wim; int ret; int image; struct stat stbuf; bool wim_is_new; STRING_LIST(refglobs); unsigned num_threads = 0; uint32_t chunk_size = UINT32_MAX; uint32_t solid_chunk_size = UINT32_MAX; int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID; for_opt(c, export_options) { switch (c) { case IMAGEX_BOOT_OPTION: export_flags |= WIMLIB_EXPORT_FLAG_BOOT; break; case IMAGEX_CHECK_OPTION: open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; /* fall-through */ case IMAGEX_INCLUDE_INTEGRITY_OPTION: write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; break; case IMAGEX_NOCHECK_OPTION: write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY; break; case IMAGEX_COMPRESS_OPTION: compression_type = get_compression_type(optarg, false); if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) goto out_err; break; case IMAGEX_RECOMPRESS_OPTION: write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS; break; case IMAGEX_SOLID_OPTION: write_flags |= WIMLIB_WRITE_FLAG_SOLID; break; case IMAGEX_NO_SOLID_SORT_OPTION: write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT; break; case IMAGEX_CHUNK_SIZE_OPTION: chunk_size = parse_chunk_size(optarg); if (chunk_size == UINT32_MAX) goto out_err; break; case IMAGEX_SOLID_CHUNK_SIZE_OPTION: solid_chunk_size = parse_chunk_size(optarg); if (solid_chunk_size == UINT32_MAX) goto out_err; break; case IMAGEX_SOLID_COMPRESS_OPTION: solid_ctype = get_compression_type(optarg, true); if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID) goto out_err; break; case IMAGEX_REF_OPTION: ret = string_list_append(&refglobs, optarg); if (ret) goto out_free_refglobs; break; case IMAGEX_THREADS_OPTION: num_threads = parse_num_threads(optarg); if (num_threads == UINT_MAX) goto out_err; break; case IMAGEX_REBUILD_OPTION: write_flags |= WIMLIB_WRITE_FLAG_REBUILD; break; case IMAGEX_PIPABLE_OPTION: write_flags |= WIMLIB_WRITE_FLAG_PIPABLE; break; case IMAGEX_NOT_PIPABLE_OPTION: write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE; break; case IMAGEX_WIMBOOT_OPTION: export_flags |= WIMLIB_EXPORT_FLAG_WIMBOOT; break; case IMAGEX_UNSAFE_COMPACT_OPTION: write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT; break; default: goto out_usage; } } argc -= optind; argv += optind; if (argc < 3 || argc > 5) goto out_usage; src_wimfile = argv[0]; src_image_num_or_name = argv[1]; dest_wimfile = argv[2]; dest_name = (argc >= 4) ? argv[3] : NULL; dest_desc = (argc >= 5) ? argv[4] : NULL; ret = wimlib_open_wim_with_progress(src_wimfile, open_flags, &src_wim, imagex_progress_func, NULL); if (ret) goto out_free_refglobs; wimlib_get_wim_info(src_wim, &src_info); /* Determine if the destination is an existing file or not. If so, we * try to append the exported image(s) to it; otherwise, we create a new * WIM containing the exported image(s). Furthermore, determine if we * need to write a pipable WIM directly to standard output. */ if (tstrcmp(dest_wimfile, T("-")) == 0) { #if 0 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) { imagex_error("Can't write a non-pipable WIM to " "standard output! Specify --pipable\n" " if you want to create a pipable WIM " "(but read the docs first)."); ret = -1; goto out_free_src_wim; } #else write_flags |= WIMLIB_WRITE_FLAG_PIPABLE; #endif dest_wimfile = NULL; dest_wim_fd = STDOUT_FILENO; imagex_output_to_stderr(); set_fd_to_binary_mode(dest_wim_fd); } errno = ENOENT; if (dest_wimfile != NULL && tstat(dest_wimfile, &stbuf) == 0) { wim_is_new = false; /* Destination file exists. */ if (!S_ISREG(stbuf.st_mode) && !S_ISBLK(stbuf.st_mode)) { imagex_error(T("\"%"TS"\" is not a regular file " "or block device"), dest_wimfile); ret = -1; goto out_free_src_wim; } ret = wimlib_open_wim_with_progress(dest_wimfile, open_flags | WIMLIB_OPEN_FLAG_WRITE_ACCESS, &dest_wim, imagex_progress_func, NULL); if (ret) goto out_free_src_wim; if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) { /* The user specified a compression type, but we're * exporting to an existing WIM. Make sure the * specified compression type is the same as the * compression type of the existing destination WIM. */ struct wimlib_wim_info dest_info; wimlib_get_wim_info(dest_wim, &dest_info); if (compression_type != dest_info.compression_type) { imagex_error(T("Cannot specify a compression type that is " "not the same as that used in the " "destination WIM")); ret = -1; goto out_free_dest_wim; } } } else { wim_is_new = true; if (errno != ENOENT) { imagex_error_with_errno(T("Cannot stat file \"%"TS"\""), dest_wimfile); ret = -1; goto out_free_src_wim; } if (write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) { imagex_error(T("'--unsafe-compact' is only valid when " "exporting to an existing WIM file!")); ret = -1; goto out_free_src_wim; } /* dest_wimfile is not an existing file, so create a new WIM. */ if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) { /* The user did not specify a compression type; default * to that of the source WIM, unless --solid or * --wimboot was specified. */ if (write_flags & WIMLIB_WRITE_FLAG_SOLID) compression_type = WIMLIB_COMPRESSION_TYPE_LZMS; else if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT) compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS; else compression_type = src_info.compression_type; } ret = wimlib_create_new_wim(compression_type, &dest_wim); if (ret) goto out_free_src_wim; wimlib_register_progress_function(dest_wim, imagex_progress_func, NULL); if ((export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT) && compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS) { /* For --wimboot export, use small XPRESS chunks. */ wimlib_set_output_chunk_size(dest_wim, 4096); } else if (compression_type == src_info.compression_type && chunk_size == UINT32_MAX) { /* Use same chunk size if compression type is the same. */ wimlib_set_output_chunk_size(dest_wim, src_info.chunk_size); } } if (chunk_size != UINT32_MAX) { /* Set destination chunk size. */ ret = wimlib_set_output_chunk_size(dest_wim, chunk_size); if (ret) goto out_free_dest_wim; } if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) { ret = wimlib_set_output_pack_compression_type(dest_wim, solid_ctype); if (ret) goto out_free_dest_wim; } if (solid_chunk_size != UINT32_MAX) { ret = wimlib_set_output_pack_chunk_size(dest_wim, solid_chunk_size); if (ret) goto out_free_dest_wim; } image = wimlib_resolve_image(src_wim, src_image_num_or_name); ret = verify_image_exists(image, src_image_num_or_name, src_wimfile); if (ret) goto out_free_dest_wim; if (refglobs.num_strings) { ret = wim_reference_globs(src_wim, &refglobs, open_flags); if (ret) goto out_free_dest_wim; } if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) && image == WIMLIB_ALL_IMAGES && src_info.boot_index == 0) { imagex_error(T("--boot specified for all-images export, but source WIM " "has no bootable image.")); ret = -1; goto out_free_dest_wim; } ret = wimlib_export_image(src_wim, image, dest_wim, dest_name, dest_desc, export_flags); if (ret) { if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) { do_resource_not_found_warning(src_wimfile, &src_info, &refglobs); } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) { do_metadata_not_found_warning(src_wimfile, &src_info); } goto out_free_dest_wim; } if (!wim_is_new) ret = wimlib_overwrite(dest_wim, write_flags, num_threads); else if (dest_wimfile) ret = wimlib_write(dest_wim, dest_wimfile, WIMLIB_ALL_IMAGES, write_flags, num_threads); else ret = wimlib_write_to_fd(dest_wim, dest_wim_fd, WIMLIB_ALL_IMAGES, write_flags, num_threads); out_free_dest_wim: wimlib_free(dest_wim); out_free_src_wim: wimlib_free(src_wim); out_free_refglobs: string_list_destroy(&refglobs); return ret; out_usage: usage(CMD_EXPORT, stderr); out_err: ret = -1; goto out_free_refglobs; } /* Extract files or directories from a WIM image */ static int imagex_extract(int argc, tchar **argv, int cmd) { int c; int open_flags = 0; int image; WIMStruct *wim; int ret; const tchar *wimfile; const tchar *image_num_or_name; tchar *dest_dir = T("."); int extract_flags = WIMLIB_EXTRACT_FLAG_NORPFIX | WIMLIB_EXTRACT_FLAG_GLOB_PATHS | WIMLIB_EXTRACT_FLAG_STRICT_GLOB; int notlist_extract_flags = WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE; STRING_LIST(refglobs); tchar *root_path = WIMLIB_WIM_ROOT_PATH; for_opt(c, extract_options) { switch (c) { case IMAGEX_CHECK_OPTION: open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; break; case IMAGEX_VERBOSE_OPTION: /* No longer does anything. */ break; case IMAGEX_REF_OPTION: ret = string_list_append(&refglobs, optarg); if (ret) goto out_free_refglobs; break; case IMAGEX_UNIX_DATA_OPTION: extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA; break; case IMAGEX_NO_ACLS_OPTION: extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS; break; case IMAGEX_STRICT_ACLS_OPTION: extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS; break; case IMAGEX_NO_ATTRIBUTES_OPTION: extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES; break; case IMAGEX_DEST_DIR_OPTION: dest_dir = optarg; break; case IMAGEX_TO_STDOUT_OPTION: extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT; imagex_suppress_output(); set_fd_to_binary_mode(STDOUT_FILENO); break; case IMAGEX_INCLUDE_INVALID_NAMES_OPTION: extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES; extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS; break; case IMAGEX_NO_GLOBS_OPTION: extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS; break; case IMAGEX_NULLGLOB_OPTION: extract_flags &= ~WIMLIB_EXTRACT_FLAG_STRICT_GLOB; break; case IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION: notlist_extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE; break; case IMAGEX_WIMBOOT_OPTION: extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT; break; case IMAGEX_COMPACT_OPTION: ret = set_compact_mode(optarg, &extract_flags); if (ret) goto out_free_refglobs; break; default: goto out_usage; } } argc -= optind; argv += optind; if (argc < 2) goto out_usage; if (!(extract_flags & (WIMLIB_EXTRACT_FLAG_GLOB_PATHS | WIMLIB_EXTRACT_FLAG_STRICT_GLOB))) { imagex_error(T("Can't combine --no-globs and --nullglob!")); goto out_err; } wimfile = argv[0]; image_num_or_name = argv[1]; argc -= 2; argv += 2; ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim, imagex_progress_func, NULL); if (ret) goto out_free_refglobs; image = wimlib_resolve_image(wim, image_num_or_name); ret = verify_image_exists_and_is_single(image, image_num_or_name, wimfile); if (ret) goto out_wimlib_free; if (refglobs.num_strings) { ret = wim_reference_globs(wim, &refglobs, open_flags); if (ret) goto out_wimlib_free; } if (argc == 0) { argv = &root_path; argc = 1; extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS; } while (argc != 0 && ret == 0) { int num_paths; for (num_paths = 0; num_paths < argc && argv[num_paths][0] != T('@'); num_paths++) ; if (num_paths) { ret = wimlib_extract_paths(wim, image, dest_dir, (const tchar **)argv, num_paths, extract_flags | notlist_extract_flags); argc -= num_paths; argv += num_paths; } else { const tchar *listfile = argv[0] + 1; if (!tstrcmp(listfile, T("-"))) { tputs(T("Reading pathlist file from standard input...")); listfile = NULL; } ret = wimlib_extract_pathlist(wim, image, dest_dir, listfile, extract_flags); argc--; argv++; } } if (ret == 0) { imagex_printf(T("Done extracting files.\n")); } else if (ret == WIMLIB_ERR_PATH_DOES_NOT_EXIST) { if ((extract_flags & (WIMLIB_EXTRACT_FLAG_STRICT_GLOB | WIMLIB_EXTRACT_FLAG_GLOB_PATHS)) == (WIMLIB_EXTRACT_FLAG_STRICT_GLOB | WIMLIB_EXTRACT_FLAG_GLOB_PATHS)) { tfprintf(stderr, T("Note: You can use the '--nullglob' " "option to ignore missing files.\n")); } tfprintf(stderr, T("Note: You can use `%"TS"' to see what " "files and directories\n" " are in the WIM image.\n"), get_cmd_string(CMD_DIR, false)); } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) { struct wimlib_wim_info info; wimlib_get_wim_info(wim, &info); do_resource_not_found_warning(wimfile, &info, &refglobs); } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) { struct wimlib_wim_info info; wimlib_get_wim_info(wim, &info); do_metadata_not_found_warning(wimfile, &info); } out_wimlib_free: wimlib_free(wim); out_free_refglobs: string_list_destroy(&refglobs); return ret; out_usage: usage(CMD_EXTRACT, stderr); out_err: ret = -1; goto out_free_refglobs; } /* Prints information about a WIM file; also can mark an image as bootable, * change the name of an image, or change the description of an image. */ static int imagex_info(int argc, tchar **argv, int cmd) { int c; bool boot = false; bool header = false; bool blobs = false; bool xml = false; bool short_header = true; const tchar *xml_out_file = NULL; const tchar *wimfile; const tchar *image_num_or_name; STRING_LIST(image_properties); WIMStruct *wim; int image; int ret; int open_flags = 0; int write_flags = 0; struct wimlib_wim_info info; for_opt(c, info_options) { switch (c) { case IMAGEX_BOOT_OPTION: boot = true; break; case IMAGEX_CHECK_OPTION: open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; /* fall-through */ case IMAGEX_INCLUDE_INTEGRITY_OPTION: write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; break; case IMAGEX_NOCHECK_OPTION: write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY; break; case IMAGEX_HEADER_OPTION: header = true; short_header = false; break; case IMAGEX_BLOBS_OPTION: blobs = true; short_header = false; break; case IMAGEX_XML_OPTION: xml = true; short_header = false; break; case IMAGEX_EXTRACT_XML_OPTION: xml_out_file = optarg; short_header = false; break; case IMAGEX_IMAGE_PROPERTY_OPTION: ret = append_image_property_argument(&image_properties); if (ret) goto out; break; default: goto out_usage; } } argc -= optind; argv += optind; if (argc < 1 || argc > 4) goto out_usage; wimfile = argv[0]; image_num_or_name = (argc >= 2) ? argv[1] : T("all"); if (argc >= 3) { /* NEW_NAME */ tchar *p = alloca((5 + tstrlen(argv[2]) + 1) * sizeof(tchar)); tsprintf(p, T("NAME=%"TS), argv[2]); ret = string_list_append(&image_properties, p); if (ret) goto out; } if (argc >= 4) { /* NEW_DESC */ tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar)); tsprintf(p, T("DESCRIPTION=%"TS), argv[3]); ret = string_list_append(&image_properties, p); if (ret) goto out; } ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim, imagex_progress_func, NULL); if (ret) goto out; wimlib_get_wim_info(wim, &info); image = wimlib_resolve_image(wim, image_num_or_name); ret = WIMLIB_ERR_INVALID_IMAGE; if (image == WIMLIB_NO_IMAGE && tstrcmp(image_num_or_name, T("0"))) { verify_image_exists(image, image_num_or_name, wimfile); if (boot) { imagex_error(T("If you would like to set the boot " "index to 0, specify image \"0\" with " "the --boot flag.")); } goto out_wimlib_free; } if (boot && info.image_count == 0) { imagex_error(T("--boot is meaningless on a WIM with no images")); goto out_wimlib_free; } if (image == WIMLIB_ALL_IMAGES && info.image_count > 1) { if (boot) { imagex_error(T("Cannot specify the --boot flag " "without specifying a specific " "image in a multi-image WIM")); goto out_wimlib_free; } if (image_properties.num_strings) { imagex_error(T("Can't change image properties without " "specifying a specific image in a " "multi-image WIM")); goto out_wimlib_free; } } /* Operations that print information are separated from operations that * recreate the WIM file. */ if (!image_properties.num_strings && !boot) { /* Read-only operations */ if (image == WIMLIB_NO_IMAGE) { imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\""), image_num_or_name, wimfile); goto out_wimlib_free; } if (image == WIMLIB_ALL_IMAGES && short_header) print_wim_information(wimfile, &info); if (header) wimlib_print_header(wim); if (blobs) { if (info.total_parts != 1) { tfprintf(stderr, T("Warning: Only showing the blobs " "for part %d of a %d-part WIM.\n"), info.part_number, info.total_parts); } print_blobs(wim); } if (xml) { ret = wimlib_extract_xml_data(wim, stdout); if (ret) goto out_wimlib_free; } if (xml_out_file) { FILE *fp; fp = tfopen(xml_out_file, T("wb")); if (!fp) { imagex_error_with_errno(T("Failed to open the " "file \"%"TS"\" for " "writing"), xml_out_file); ret = -1; goto out_wimlib_free; } ret = wimlib_extract_xml_data(wim, fp); if (fclose(fp)) { imagex_error(T("Failed to close the file " "\"%"TS"\""), xml_out_file); ret = -1; } if (ret) goto out_wimlib_free; } if (short_header) wimlib_print_available_images(wim, image); ret = 0; } else { /* Modification operations */ bool any_property_changes; if (image == WIMLIB_ALL_IMAGES) image = 1; if (image == WIMLIB_NO_IMAGE && image_properties.num_strings) { imagex_error(T("Cannot change image properties " "when using image 0")); ret = -1; goto out_wimlib_free; } if (boot) { if (image == info.boot_index) { imagex_printf(T("Image %d is already marked as " "bootable.\n"), image); boot = false; } else { imagex_printf(T("Marking image %d as bootable.\n"), image); info.boot_index = image; ret = wimlib_set_wim_info(wim, &info, WIMLIB_CHANGE_BOOT_INDEX); if (ret) goto out_wimlib_free; } } ret = apply_image_properties(&image_properties, wim, image, &any_property_changes); if (ret) goto out_wimlib_free; /* Only call wimlib_overwrite() if something actually needs to * be changed. */ if (boot || any_property_changes || ((write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) && !info.has_integrity_table) || ((write_flags & WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY) && info.has_integrity_table)) { ret = wimlib_overwrite(wim, write_flags, 1); } else { imagex_printf(T("The file \"%"TS"\" was not modified " "because nothing needed to be done.\n"), wimfile); ret = 0; } } out_wimlib_free: wimlib_free(wim); out: string_list_destroy(&image_properties); return ret; out_usage: usage(CMD_INFO, stderr); ret = -1; goto out; } /* Join split WIMs into one part WIM */ static int imagex_join(int argc, tchar **argv, int cmd) { int c; int swm_open_flags = 0; int wim_write_flags = 0; const tchar *output_path; int ret; for_opt(c, join_options) { switch (c) { case IMAGEX_CHECK_OPTION: swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; /* fall-through */ case IMAGEX_INCLUDE_INTEGRITY_OPTION: wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; break; default: goto out_usage; } } argc -= optind; argv += optind; if (argc < 2) { imagex_error(T("Must specify one or more split WIM (.swm) " "parts to join")); goto out_usage; } output_path = argv[0]; ret = wimlib_join_with_progress((const tchar * const *)++argv, --argc, output_path, swm_open_flags, wim_write_flags, imagex_progress_func, NULL); out: return ret; out_usage: usage(CMD_JOIN, stderr); ret = -1; goto out; } #if WIM_MOUNTING_SUPPORTED /* Mounts a WIM image. */ static int imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd) { int c; int mount_flags = 0; int open_flags = 0; const tchar *staging_dir = NULL; const tchar *wimfile; const tchar *dir; WIMStruct *wim; struct wimlib_wim_info info; int image; int ret; STRING_LIST(refglobs); if (cmd == CMD_MOUNTRW) { mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE; open_flags |= WIMLIB_OPEN_FLAG_WRITE_ACCESS; } for_opt(c, mount_options) { switch (c) { case IMAGEX_ALLOW_OTHER_OPTION: mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER; break; case IMAGEX_CHECK_OPTION: open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; break; case IMAGEX_DEBUG_OPTION: mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG; break; case IMAGEX_STREAMS_INTERFACE_OPTION: if (!tstrcasecmp(optarg, T("none"))) mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE; else if (!tstrcasecmp(optarg, T("xattr"))) mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR; else if (!tstrcasecmp(optarg, T("windows"))) mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS; else { imagex_error(T("Unknown stream interface \"%"TS"\""), optarg); goto out_usage; } break; case IMAGEX_REF_OPTION: ret = string_list_append(&refglobs, optarg); if (ret) goto out_free_refglobs; break; case IMAGEX_STAGING_DIR_OPTION: staging_dir = optarg; break; case IMAGEX_UNIX_DATA_OPTION: mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA; break; default: goto out_usage; } } argc -= optind; argv += optind; if (argc != 2 && argc != 3) goto out_usage; wimfile = argv[0]; ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim, imagex_progress_func, NULL); if (ret) goto out_free_refglobs; wimlib_get_wim_info(wim, &info); if (argc >= 3) { /* Image explicitly specified. */ image = wimlib_resolve_image(wim, argv[1]); dir = argv[2]; ret = verify_image_exists_and_is_single(image, argv[1], wimfile); if (ret) goto out_free_wim; } else { /* No image specified; default to image 1, but only if the WIM * contains exactly one image. */ if (info.image_count != 1) { imagex_error(T("\"%"TS"\" contains %d images; Please " "select one."), wimfile, info.image_count); wimlib_free(wim); goto out_usage; } image = 1; dir = argv[1]; } if (refglobs.num_strings) { ret = wim_reference_globs(wim, &refglobs, open_flags); if (ret) goto out_free_wim; } ret = wimlib_mount_image(wim, image, dir, mount_flags, staging_dir); if (ret) { if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) { do_metadata_not_found_warning(wimfile, &info); } else { imagex_error(T("Failed to mount image %d from \"%"TS"\" " "on \"%"TS"\""), image, wimfile, dir); } } out_free_wim: wimlib_free(wim); out_free_refglobs: string_list_destroy(&refglobs); return ret; out_usage: usage(cmd, stderr); ret = -1; goto out_free_refglobs; } #endif /* WIM_MOUNTING_SUPPORTED */ /* Rebuild a WIM file */ static int imagex_optimize(int argc, tchar **argv, int cmd) { int c; int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS; int write_flags = WIMLIB_WRITE_FLAG_REBUILD; int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID; uint32_t chunk_size = UINT32_MAX; uint32_t solid_chunk_size = UINT32_MAX; int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID; int ret; WIMStruct *wim; const tchar *wimfile; off_t old_size; off_t new_size; unsigned num_threads = 0; for_opt(c, optimize_options) { switch (c) { case IMAGEX_CHECK_OPTION: open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; /* fall-through */ case IMAGEX_INCLUDE_INTEGRITY_OPTION: write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; break; case IMAGEX_NOCHECK_OPTION: write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY; break; case IMAGEX_COMPRESS_OPTION: write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS; compression_type = get_compression_type(optarg, false); if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) goto out_err; break; case IMAGEX_RECOMPRESS_OPTION: write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS; break; case IMAGEX_CHUNK_SIZE_OPTION: chunk_size = parse_chunk_size(optarg); if (chunk_size == UINT32_MAX) goto out_err; break; case IMAGEX_SOLID_CHUNK_SIZE_OPTION: solid_chunk_size = parse_chunk_size(optarg); if (solid_chunk_size == UINT32_MAX) goto out_err; break; case IMAGEX_SOLID_COMPRESS_OPTION: solid_ctype = get_compression_type(optarg, true); if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID) goto out_err; break; case IMAGEX_SOLID_OPTION: write_flags |= WIMLIB_WRITE_FLAG_SOLID; write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS; break; case IMAGEX_NO_SOLID_SORT_OPTION: write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT; break; case IMAGEX_THREADS_OPTION: num_threads = parse_num_threads(optarg); if (num_threads == UINT_MAX) goto out_err; break; case IMAGEX_PIPABLE_OPTION: write_flags |= WIMLIB_WRITE_FLAG_PIPABLE; break; case IMAGEX_NOT_PIPABLE_OPTION: write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE; break; case IMAGEX_UNSAFE_COMPACT_OPTION: write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT; break; default: goto out_usage; } } argc -= optind; argv += optind; if (argc != 1) goto out_usage; wimfile = argv[0]; ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim, imagex_progress_func, NULL); if (ret) goto out; if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) { /* Change compression type. */ ret = wimlib_set_output_compression_type(wim, compression_type); if (ret) goto out_wimlib_free; } if (chunk_size != UINT32_MAX) { /* Change chunk size. */ ret = wimlib_set_output_chunk_size(wim, chunk_size); if (ret) goto out_wimlib_free; } if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) { ret = wimlib_set_output_pack_compression_type(wim, solid_ctype); if (ret) goto out_wimlib_free; } if (solid_chunk_size != UINT32_MAX) { ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size); if (ret) goto out_wimlib_free; } old_size = file_get_size(wimfile); tprintf(T("\"%"TS"\" original size: "), wimfile); if (old_size == -1) tputs(T("Unknown")); else tprintf(T("%"PRIu64" KiB\n"), old_size >> 10); ret = wimlib_overwrite(wim, write_flags, num_threads); if (ret) { imagex_error(T("Optimization of \"%"TS"\" failed."), wimfile); goto out_wimlib_free; } new_size = file_get_size(wimfile); tprintf(T("\"%"TS"\" optimized size: "), wimfile); if (new_size == -1) tputs(T("Unknown")); else tprintf(T("%"PRIu64" KiB\n"), new_size >> 10); tfputs(T("Space saved: "), stdout); if (new_size != -1 && old_size != -1) { tprintf(T("%lld KiB\n"), ((long long)old_size - (long long)new_size) >> 10); } else { tputs(T("Unknown")); } ret = 0; out_wimlib_free: wimlib_free(wim); out: return ret; out_usage: usage(CMD_OPTIMIZE, stderr); out_err: ret = -1; goto out; } /* Split a WIM into a spanned set */ static int imagex_split(int argc, tchar **argv, int cmd) { int c; int open_flags = 0; int write_flags = 0; unsigned long part_size; tchar *tmp; int ret; WIMStruct *wim; for_opt(c, split_options) { switch (c) { case IMAGEX_CHECK_OPTION: open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; /* fall-through */ case IMAGEX_INCLUDE_INTEGRITY_OPTION: write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; break; default: goto out_usage; } } argc -= optind; argv += optind; if (argc != 3) goto out_usage; part_size = tstrtod(argv[2], &tmp) * (1 << 20); if (tmp == argv[2] || *tmp) { imagex_error(T("Invalid part size \"%"TS"\""), argv[2]); imagex_error(T("The part size must be an integer or " "floating-point number of megabytes.")); goto out_err; } ret = wimlib_open_wim_with_progress(argv[0], open_flags, &wim, imagex_progress_func, NULL); if (ret) goto out; ret = wimlib_split(wim, argv[1], part_size, write_flags); wimlib_free(wim); out: return ret; out_usage: usage(CMD_SPLIT, stderr); out_err: ret = -1; goto out; } #if WIM_MOUNTING_SUPPORTED /* Unmounts a mounted WIM image. */ static int imagex_unmount(int argc, tchar **argv, int cmd) { int c; int unmount_flags = 0; int ret; for_opt(c, unmount_options) { switch (c) { case IMAGEX_COMMIT_OPTION: unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT; break; case IMAGEX_CHECK_OPTION: unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY; break; case IMAGEX_REBUILD_OPTION: unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD; break; case IMAGEX_LAZY_OPTION: case IMAGEX_FORCE_OPTION: /* Now, unmount is lazy by default. However, committing * the image will fail with * WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY if there are open * file descriptors on the WIM image. The * WIMLIB_UNMOUNT_FLAG_FORCE option forces these file * descriptors to be closed. */ unmount_flags |= WIMLIB_UNMOUNT_FLAG_FORCE; break; case IMAGEX_NEW_IMAGE_OPTION: unmount_flags |= WIMLIB_UNMOUNT_FLAG_NEW_IMAGE; break; default: goto out_usage; } } argc -= optind; argv += optind; if (argc != 1) goto out_usage; if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) { if (!(unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT)) { imagex_error(T("--new-image is meaningless " "without --commit also specified!")); goto out_err; } } ret = wimlib_unmount_image_with_progress(argv[0], unmount_flags, imagex_progress_func, NULL); if (ret) { imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]); if (ret == WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY) { imagex_printf(T( "\tNote: Use --commit --force to force changes " "to be committed, regardless\n" "\t of open files.\n")); } } out: return ret; out_usage: usage(CMD_UNMOUNT, stderr); out_err: ret = -1; goto out; } #endif /* WIM_MOUNTING_SUPPORTED */ /* * Add, delete, or rename files in a WIM image. */ static int imagex_update(int argc, tchar **argv, int cmd) { const tchar *wimfile; int image; WIMStruct *wim; int ret; int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS; int write_flags = 0; int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS; int default_add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE | WIMLIB_ADD_FLAG_VERBOSE | WIMLIB_ADD_FLAG_WINCONFIG; int default_delete_flags = 0; unsigned num_threads = 0; int c; tchar *cmd_file_contents; size_t cmd_file_nchars; struct wimlib_update_command *cmds; size_t num_cmds; tchar *command_str = NULL; tchar *config_file = NULL; tchar *wimboot_config = NULL; for_opt(c, update_options) { switch (c) { /* Generic or write options */ case IMAGEX_THREADS_OPTION: num_threads = parse_num_threads(optarg); if (num_threads == UINT_MAX) goto out_err; break; case IMAGEX_CHECK_OPTION: open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; /* fall-through */ case IMAGEX_INCLUDE_INTEGRITY_OPTION: write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; break; case IMAGEX_REBUILD_OPTION: write_flags |= WIMLIB_WRITE_FLAG_REBUILD; break; case IMAGEX_COMMAND_OPTION: if (command_str) { imagex_error(T("--command may only be specified " "one time. Please provide\n" " the update commands " "on standard input instead.")); goto out_err; } command_str = tstrdup(optarg); if (!command_str) { imagex_error(T("Out of memory!")); goto out_err; } break; case IMAGEX_WIMBOOT_CONFIG_OPTION: wimboot_config = optarg; break; /* Default delete options */ case IMAGEX_FORCE_OPTION: default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE; break; case IMAGEX_RECURSIVE_OPTION: default_delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE; break; /* Global add option */ case IMAGEX_CONFIG_OPTION: default_add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG; config_file = optarg; break; /* Default add options */ case IMAGEX_VERBOSE_OPTION: /* No longer does anything. */ break; case IMAGEX_DEREFERENCE_OPTION: default_add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE; break; case IMAGEX_UNIX_DATA_OPTION: default_add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA; break; case IMAGEX_NO_ACLS_OPTION: default_add_flags |= WIMLIB_ADD_FLAG_NO_ACLS; break; case IMAGEX_STRICT_ACLS_OPTION: default_add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS; break; case IMAGEX_NO_REPLACE_OPTION: default_add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE; break; case IMAGEX_UNSAFE_COMPACT_OPTION: write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT; break; default: goto out_usage; } } argv += optind; argc -= optind; if (argc != 1 && argc != 2) goto out_usage; wimfile = argv[0]; ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim, imagex_progress_func, NULL); if (ret) goto out_free_command_str; if (argc >= 2) { /* Image explicitly specified. */ image = wimlib_resolve_image(wim, argv[1]); ret = verify_image_exists_and_is_single(image, argv[1], wimfile); if (ret) goto out_wimlib_free; } else { /* No image specified; default to image 1, but only if the WIM * contains exactly one image. */ struct wimlib_wim_info info; wimlib_get_wim_info(wim, &info); if (info.image_count != 1) { imagex_error(T("\"%"TS"\" contains %d images; Please select one."), wimfile, info.image_count); wimlib_free(wim); goto out_usage; } image = 1; } /* Read update commands from standard input, or the command string if * specified. */ if (command_str) { cmd_file_contents = NULL; cmds = parse_update_command_file(&command_str, tstrlen(command_str), &num_cmds); if (!cmds) { ret = -1; goto out_free_cmd_file_contents; } } else if (!wimboot_config) { if (isatty(STDIN_FILENO)) { tputs(T("Reading update commands from standard input...")); recommend_man_page(CMD_UPDATE, stdout); } cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars); if (!cmd_file_contents) { ret = -1; goto out_wimlib_free; } /* Parse the update commands */ cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars, &num_cmds); if (!cmds) { ret = -1; goto out_free_cmd_file_contents; } } else { cmd_file_contents = NULL; cmds = NULL; num_cmds = 0; } /* Set default flags and capture config on the update commands */ for (size_t i = 0; i < num_cmds; i++) { switch (cmds[i].op) { case WIMLIB_UPDATE_OP_ADD: cmds[i].add.add_flags |= default_add_flags; cmds[i].add.config_file = config_file; break; case WIMLIB_UPDATE_OP_DELETE: cmds[i].delete_.delete_flags |= default_delete_flags; break; default: break; } } /* Execute the update commands */ ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags); if (ret) goto out_free_cmds; if (wimboot_config) { /* --wimboot-config=FILE is short for an * "add FILE /Windows/System32/WimBootCompress.ini" command. */ struct wimlib_update_command cmd; cmd.op = WIMLIB_UPDATE_OP_ADD; cmd.add.fs_source_path = wimboot_config; cmd.add.wim_target_path = T("/Windows/System32/WimBootCompress.ini"); cmd.add.config_file = NULL; cmd.add.add_flags = 0; ret = wimlib_update_image(wim, image, &cmd, 1, update_flags); if (ret) goto out_free_cmds; } /* Overwrite the updated WIM */ ret = wimlib_overwrite(wim, write_flags, num_threads); out_free_cmds: free(cmds); out_free_cmd_file_contents: free(cmd_file_contents); out_wimlib_free: wimlib_free(wim); out_free_command_str: free(command_str); return ret; out_usage: usage(CMD_UPDATE, stderr); out_err: ret = -1; goto out_free_command_str; } /* Verify a WIM file. */ static int imagex_verify(int argc, tchar **argv, int cmd) { int ret; const tchar *wimfile; WIMStruct *wim; int open_flags = WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; int verify_flags = 0; STRING_LIST(refglobs); int c; for_opt(c, verify_options) { switch (c) { case IMAGEX_REF_OPTION: ret = string_list_append(&refglobs, optarg); if (ret) goto out_free_refglobs; break; case IMAGEX_NOCHECK_OPTION: open_flags &= ~WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; break; default: goto out_usage; } } argv += optind; argc -= optind; if (argc != 1) { if (argc == 0) imagex_error(T("Must specify a WIM file!")); else imagex_error(T("At most one WIM file can be specified!")); goto out_usage; } wimfile = argv[0]; ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim, imagex_progress_func, NULL); if (ret) goto out_free_refglobs; ret = wim_reference_globs(wim, &refglobs, open_flags); if (ret) goto out_wimlib_free; ret = wimlib_verify_wim(wim, verify_flags); if (ret) { tputc(T('\n'), stderr); imagex_error(T("\"%"TS"\" failed verification!"), wimfile); if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND && refglobs.num_strings == 0) { imagex_printf(T("Note: if this WIM file is not standalone, " "use the --ref option to specify the other parts.\n")); } } else { imagex_printf(T("\n\"%"TS"\" was successfully verified.\n"), wimfile); } out_wimlib_free: wimlib_free(wim); out_free_refglobs: string_list_destroy(&refglobs); return ret; out_usage: usage(CMD_VERIFY, stderr); ret = -1; goto out_free_refglobs; } struct imagex_command { const tchar *name; int (*func)(int argc, tchar **argv, int cmd); }; static const struct imagex_command imagex_commands[] = { [CMD_APPEND] = {T("append"), imagex_capture_or_append}, [CMD_APPLY] = {T("apply"), imagex_apply}, [CMD_CAPTURE] = {T("capture"), imagex_capture_or_append}, [CMD_DELETE] = {T("delete"), imagex_delete}, [CMD_DIR ] = {T("dir"), imagex_dir}, [CMD_EXPORT] = {T("export"), imagex_export}, [CMD_EXTRACT] = {T("extract"), imagex_extract}, [CMD_INFO] = {T("info"), imagex_info}, [CMD_JOIN] = {T("join"), imagex_join}, #if WIM_MOUNTING_SUPPORTED [CMD_MOUNT] = {T("mount"), imagex_mount_rw_or_ro}, [CMD_MOUNTRW] = {T("mountrw"), imagex_mount_rw_or_ro}, #endif [CMD_OPTIMIZE] = {T("optimize"), imagex_optimize}, [CMD_SPLIT] = {T("split"), imagex_split}, #if WIM_MOUNTING_SUPPORTED [CMD_UNMOUNT] = {T("unmount"), imagex_unmount}, #endif [CMD_UPDATE] = {T("update"), imagex_update}, [CMD_VERIFY] = {T("verify"), imagex_verify}, }; #ifdef __WIN32__ /* Can be a directory or source list file. But source list file is probably * a rare use case, so just say directory. */ # define SOURCE_STR T("DIRECTORY") /* Can only be a directory */ # define TARGET_STR T("DIRECTORY") #else /* Can be a directory, NTFS volume, or source list file. */ # define SOURCE_STR T("SOURCE") /* Can be a directory or NTFS volume. */ # define TARGET_STR T("TARGET") #endif static const tchar * const usage_strings[] = { [CMD_APPEND] = T( " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n" " [--boot] [--check] [--nocheck] [--config=FILE]\n" " [--threads=NUM_THREADS] [--no-acls] [--strict-acls]\n" " [--rpfix] [--norpfix] [--update-of=[WIMFILE:]IMAGE]\n" " [--delta-from=WIMFILE] [--wimboot] [--unix-data]\n" " [--dereference] [--snapshot] [--create]\n" ), [CMD_APPLY] = T( " %"TS" WIMFILE [IMAGE] " TARGET_STR "\n" " [--check] [--ref=\"GLOB\"] [--no-acls] [--strict-acls]\n" " [--no-attributes] [--rpfix] [--norpfix]\n" " [--include-invalid-names] [--wimboot] [--unix-data]\n" " [--compact=FORMAT]\n" ), [CMD_CAPTURE] = T( " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n" " [--compress=TYPE] [--boot] [--check] [--nocheck]\n" " [--config=FILE] [--threads=NUM_THREADS]\n" " [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n" " [--update-of=[WIMFILE:]IMAGE] [--delta-from=WIMFILE]\n" " [--wimboot] [--unix-data] [--dereference] [--solid]\n" " [--snapshot]\n" ), [CMD_DELETE] = T( " %"TS" WIMFILE IMAGE [--check] [--soft]\n" ), [CMD_DIR] = T( " %"TS" WIMFILE [IMAGE] [--path=PATH] [--detailed]\n" ), [CMD_EXPORT] = T( " %"TS" SRC_WIMFILE SRC_IMAGE DEST_WIMFILE\n" " [DEST_IMAGE_NAME [DEST_IMAGE_DESC]]\n" " [--boot] [--check] [--nocheck] [--compress=TYPE]\n" " [--ref=\"GLOB\"] [--threads=NUM_THREADS] [--rebuild]\n" " [--wimboot] [--solid]\n" ), [CMD_EXTRACT] = T( " %"TS" WIMFILE IMAGE [(PATH | @LISTFILE)...]\n" " [--check] [--ref=\"GLOB\"] [--dest-dir=CMD_DIR]\n" " [--to-stdout] [--no-acls] [--strict-acls]\n" " [--no-attributes] [--include-invalid-names]\n" " [--no-globs] [--nullglob] [--preserve-dir-structure]\n" ), [CMD_INFO] = T( " %"TS" WIMFILE [IMAGE [NEW_NAME [NEW_DESC]]]\n" " [--boot] [--check] [--nocheck] [--xml]\n" " [--extract-xml FILE] [--header] [--blobs]\n" " [--image-property NAME=VALUE]\n" ), [CMD_JOIN] = T( " %"TS" OUT_WIMFILE SPLIT_WIM_PART... [--check]\n" ), #if WIM_MOUNTING_SUPPORTED [CMD_MOUNT] = T( " %"TS" WIMFILE [IMAGE] DIRECTORY\n" " [--check] [--streams-interface=INTERFACE]\n" " [--ref=\"GLOB\"] [--allow-other] [--unix-data]\n" ), [CMD_MOUNTRW] = T( " %"TS" WIMFILE [IMAGE] DIRECTORY\n" " [--check] [--streams-interface=INTERFACE]\n" " [--staging-dir=CMD_DIR] [--allow-other] [--unix-data]\n" ), #endif [CMD_OPTIMIZE] = T( " %"TS" WIMFILE\n" " [--recompress] [--compress=TYPE] [--threads=NUM_THREADS]\n" " [--check] [--nocheck] [--solid]\n" "\n" ), [CMD_SPLIT] = T( " %"TS" WIMFILE SPLIT_WIM_PART_1 PART_SIZE_MB [--check]\n" ), #if WIM_MOUNTING_SUPPORTED [CMD_UNMOUNT] = T( " %"TS" DIRECTORY\n" " [--commit] [--force] [--new-image] [--check] [--rebuild]\n" ), #endif [CMD_UPDATE] = T( " %"TS" WIMFILE [IMAGE]\n" " [--check] [--rebuild] [--threads=NUM_THREADS]\n" " [DEFAULT_ADD_OPTIONS] [DEFAULT_DELETE_OPTIONS]\n" " [--command=STRING] [--wimboot-config=FILE]\n" " [< CMDFILE]\n" ), [CMD_VERIFY] = T( " %"TS" WIMFILE [--ref=\"GLOB\"]\n" ), }; static const tchar *invocation_name; static int invocation_cmd = CMD_NONE; static const tchar *get_cmd_string(int cmd, bool only_short_form) { static tchar buf[50]; if (cmd == CMD_NONE) return T("wimlib-imagex"); if (only_short_form || invocation_cmd != CMD_NONE) { tsprintf(buf, T("wim%"TS), imagex_commands[cmd].name); } else { tsprintf(buf, T("%"TS" %"TS), invocation_name, imagex_commands[cmd].name); } return buf; } static void version(void) { static const tchar * const fmt = T( "wimlib-imagex " PACKAGE_VERSION " (using wimlib %"TS")\n" "Copyright (C) 2012-2018 Eric Biggers\n" "License GPLv3+; GNU GPL version 3 or later .\n" "This is free software: you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law.\n" "\n" "Report bugs to "PACKAGE_BUGREPORT".\n" ); tfprintf(stdout, fmt, wimlib_get_version_string()); } static void do_common_options(int *argc_p, tchar **argv, int cmd) { int argc = *argc_p; int i; const tchar *p; for (i = 1; i < argc; i++) { p = argv[i]; if (p[0] == T('-') && p[1] == T('-')) { p += 2; if (!tstrcmp(p, T("help"))) { if (cmd == CMD_NONE) usage_all(stdout); else usage(cmd, stdout); exit(0); } else if (!tstrcmp(p, T("version"))) { version(); exit(0); } else if (!tstrcmp(p, T("quiet"))) { imagex_suppress_output(); memmove(&argv[i], &argv[i + 1], (argc - i) * sizeof(argv[i])); argc--; i--; } else if (!*p) /* reached "--", no more options */ break; } } *argc_p = argc; } static void print_usage_string(int cmd, FILE *fp) { tfprintf(fp, usage_strings[cmd], get_cmd_string(cmd, false)); } static void recommend_man_page(int cmd, FILE *fp) { const tchar *format_str; #ifdef __WIN32__ format_str = T("Some uncommon options are not listed;\n" "See %"TS".pdf in the doc directory for more details.\n"); #else format_str = T("Some uncommon options are not listed; see `man %"TS"' for more details.\n"); #endif tfprintf(fp, format_str, get_cmd_string(cmd, true)); } static void usage(int cmd, FILE *fp) { tfprintf(fp, T("Usage:\n")); print_usage_string(cmd, fp); tfprintf(fp, T("\n")); recommend_man_page(cmd, fp); } static void usage_all(FILE *fp) { tfprintf(fp, T("Usage:\n")); for (int cmd = 0; cmd < CMD_MAX; cmd++) { print_usage_string(cmd, fp); tfprintf(fp, T("\n")); } static const tchar * const extra = T( " %"TS" --help\n" " %"TS" --version\n" "\n" ); tfprintf(fp, extra, invocation_name, invocation_name); tfprintf(fp, T("IMAGE can be the 1-based index or name of an image in the WIM file.\n" "For some commands IMAGE is optional if the WIM file only contains one image.\n" "For some commands IMAGE may be \"all\".\n" "\n")); recommend_man_page(CMD_NONE, fp); } #ifdef __WIN32__ extern int wmain(int argc, wchar_t **argv); #define main wmain #endif /* Entry point for wimlib's ImageX implementation. On UNIX the command * arguments will just be 'char' strings (ideally UTF-8 encoded, but could be * something else), while on Windows the command arguments will be UTF-16LE * encoded 'wchar_t' strings. */ int main(int argc, tchar **argv) { int ret; int init_flags = 0; int cmd; imagex_info_file = stdout; invocation_name = tbasename(argv[0]); { tchar *igcase = tgetenv(T("WIMLIB_IMAGEX_IGNORE_CASE")); if (igcase != NULL) { if (!tstrcmp(igcase, T("no")) || !tstrcmp(igcase, T("0"))) init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE; else if (!tstrcmp(igcase, T("yes")) || !tstrcmp(igcase, T("1"))) init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE; else { fprintf(stderr, "WARNING: Ignoring unknown setting of " "WIMLIB_IMAGEX_IGNORE_CASE\n"); } } } /* Allow being invoked as wimCOMMAND (e.g. wimapply). */ cmd = CMD_NONE; if (!tstrncmp(invocation_name, T("wim"), 3) && tstrcmp(invocation_name, T("wimlib-imagex"))) { for (int i = 0; i < CMD_MAX; i++) { if (!tstrcmp(invocation_name + 3, imagex_commands[i].name)) { invocation_cmd = i; cmd = i; break; } } } /* Unless already known from the invocation name, determine which * command was specified. */ if (cmd == CMD_NONE) { if (argc < 2) { imagex_error(T("No command specified!\n")); usage_all(stderr); exit(2); } for (int i = 0; i < CMD_MAX; i++) { if (!tstrcmp(argv[1], imagex_commands[i].name)) { cmd = i; break; } } if (cmd != CMD_NONE) { argc--; argv++; } } /* Handle common options. May exit early (for --help or --version). */ do_common_options(&argc, argv, cmd); /* Bail if a valid command was not specified. */ if (cmd == CMD_NONE) { imagex_error(T("Unrecognized command: `%"TS"'\n"), argv[1]); usage_all(stderr); exit(2); } /* Enable warning and error messages in wimlib to be more user-friendly. * */ wimlib_set_print_errors(true); /* Initialize wimlib. */ ret = wimlib_global_init(init_flags); if (ret) goto out_check_status; /* Call the command handler function. */ ret = imagex_commands[cmd].func(argc, argv, cmd); /* Check for error writing to standard output, especially since for some * commands, writing to standard output is part of the program's actual * behavior and not just for informational purposes. */ if (ferror(stdout) || fclose(stdout)) { imagex_error_with_errno(T("error writing to standard output")); if (ret == 0) ret = -1; } out_check_status: /* Exit status (ret): -1 indicates an error found by 'wimlib-imagex' * itself (not by wimlib). 0 indicates success. > 0 indicates a wimlib * error code from which an error message can be printed. */ if (ret > 0) { imagex_error(T("Exiting with error code %d:\n" " %"TS"."), ret, wimlib_get_error_string(ret)); if (ret == WIMLIB_ERR_NTFS_3G && errno != 0) imagex_error_with_errno(T("errno")); } /* Make wimlib free any resources it's holding (although this is not * strictly necessary because the process is ending anyway). */ wimlib_global_cleanup(); return ret; } wimlib-1.13.1/programs/wgetopt.c0000644000175000017500000005373013016523676013511 00000000000000/* * wgetopt.c: Wide-character versions of getopt, getopt_long, and * getopt_long_only. * * This has been modified from the original, which is * Copyright (C) 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, * 1997, 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010 Free Software Foundation, Inc. * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 3 of the License, or (at your option) any later * version. * * This file is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this file; if not, see http://www.gnu.org/licenses/. */ #include "wgetopt.h" #include #include /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ wchar_t *woptarg = NULL; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `woptind' communicates from one call to the next how much of ARGV has been scanned so far. */ /* 1003.2 says this must be 1 before any call. */ int woptind = 1; /* Formerly, initialization of getopt depended on woptind==0, which causes problems with re-calling getopt as programs generally don't know that. */ int __getopt_initialized = 0; /* The next char to be scanned in the option-element in which the last option character we returned was found. This allows us to pick up the scan where we left off. If this is zero, or a null string, it means resume the scan by advancing to the next ARGV-element. */ static wchar_t *nextchar; /* Callers store zero here to inhibit the error message for unrecognized options. */ int wopterr = 1; /* Set to an option character which was unrecognized. This must be initialized on some systems to avoid linking in the system's own getopt implementation. */ int woptopt = '?'; /* Describe how to deal with options that follow non-option ARGV-elements. If the caller did not specify anything, the default is REQUIRE_ORDER if the environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise. REQUIRE_ORDER means don't recognize them as options; stop option processing when the first non-option is seen. This is what Unix does. This mode of operation is selected by either setting the environment variable POSIXLY_CORRECT, or using `+' as the first character of the list of option characters. PERMUTE is the default. We permute the contents of ARGV as we scan, so that eventually all the non-options are at the end. This allows options to be given in any order, even with programs that were not written to expect this. RETURN_IN_ORDER is an option available to programs that were written to expect options and other ARGV-elements in any order and that care about the ordering of the two. We describe each non-option ARGV-element as if it were the argument of an option with character code 1. Using `-' as the first character of the list of option characters selects this mode of operation. The special argument `--' forces an end of option-scanning regardless of the value of `ordering'. In the case of RETURN_IN_ORDER, only `--' can cause `getopt' to return -1 with `woptind' != ARGC. */ static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; /* Value of POSIXLY_CORRECT environment variable. */ static char *posixly_correct; /* Handle permutation of arguments. */ /* Describe the part of ARGV that contains non-options that have been skipped. `first_nonopt' is the index in ARGV of the first of them; `last_nonopt' is the index after the last of them. */ static int first_nonopt; static int last_nonopt; /* Exchange two adjacent subsequences of ARGV. One subsequence is elements [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The other is elements [last_nonopt,woptind), which contains all the options processed since those non-options were skipped. `first_nonopt' and `last_nonopt' are relocated so that they describe the new indices of the non-options in ARGV after they are moved. */ static void exchange (wchar_t **argv) { int bottom = first_nonopt; int middle = last_nonopt; int top = woptind; wchar_t *tem; /* Exchange the shorter segment with the far end of the longer segment. That puts the shorter segment into the right place. It leaves the longer segment in the right place overall, but it consists of two parts that need to be swapped next. */ while (top > middle && middle > bottom) { if (top - middle > middle - bottom) { /* Bottom segment is the short one. */ int len = middle - bottom; register int i; /* Swap it with the top part of the top segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[top - (middle - bottom) + i]; argv[top - (middle - bottom) + i] = tem; } /* Exclude the moved bottom segment from further swapping. */ top -= len; } else { /* Top segment is the short one. */ int len = top - middle; register int i; /* Swap it with the bottom part of the bottom segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[middle + i]; argv[middle + i] = tem; } /* Exclude the moved top segment from further swapping. */ bottom += len; } } /* Update records for the slots the non-options now occupy. */ first_nonopt += (woptind - last_nonopt); last_nonopt = woptind; } /* Initialize the internal data when the first call is made. */ static const wchar_t * _getopt_initialize (int argc, wchar_t *const *argv, const wchar_t *optstring) { /* Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped non-option ARGV-elements is empty. */ first_nonopt = last_nonopt = woptind; nextchar = NULL; posixly_correct = getenv ("POSIXLY_CORRECT"); /* Determine how to handle the ordering of options and nonoptions. */ if (optstring[0] == L'-') { ordering = RETURN_IN_ORDER; ++optstring; } else if (optstring[0] == L'+') { ordering = REQUIRE_ORDER; ++optstring; } else if (posixly_correct != NULL) ordering = REQUIRE_ORDER; else ordering = PERMUTE; return optstring; } /* Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option element. The characters of this element (aside from the initial '-') are option characters. If `getopt' is called repeatedly, it returns successively each of the option characters from each of the option elements. If `getopt' finds another option character, it returns that character, updating `woptind' and `nextchar' so that the next call to `getopt' can resume the scan with the following option character or ARGV-element. If there are no more option characters, `getopt' returns -1. Then `woptind' is the index in ARGV of the first ARGV-element that is not an option. (The ARGV-elements have been permuted so that those that are not options now come last.) OPTSTRING is a string containing the legitimate option characters. If an option character is seen that is not listed in OPTSTRING, return '?' after printing an error message. If you set `wopterr' to zero, the error message is suppressed but we still return '?'. If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text in the same ARGV-element, or the text of the following ARGV-element, is returned in `woptarg'. Two colons mean an option that wants an optional arg; if there is text in the current ARGV-element, it is returned in `woptarg', otherwise `woptarg' is set to zero. If OPTSTRING starts with `-' or `+', it requests different methods of handling the non-option ARGV-elements. See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. Long-named options begin with `--' instead of `-'. Their names may be abbreviated as long as the abbreviation is unique or is an exact match for some defined option. If they have an argument, it follows the option name in the same ARGV-element, separated from the option name by a `=', or else the in next ARGV-element. When `getopt' finds a long-named option, it returns 0 if that option's `flag' field is nonzero, the value of the option's `val' field if the `flag' field is zero. The elements of ARGV aren't really const, because we permute them. But we pretend they're const in the prototype to be compatible with other systems. LONGOPTS is a vector of `struct woption' terminated by an element containing a name which is zero. LONGIND returns the index in LONGOPT of the long-named option found. It is only valid when a long-named option has been found by the most recent call. If LONG_ONLY is nonzero, '-' as well as '--' can introduce long-named options. */ static int _wgetopt_internal (int argc, wchar_t *const *argv, const wchar_t *optstring, const struct woption *longopts, int *longind, int long_only) { woptarg = NULL; if (woptind == 0 || !__getopt_initialized) { if (woptind == 0) woptind = 1; /* Don't scan ARGV[0], the program name. */ optstring = _getopt_initialize (argc, argv, optstring); __getopt_initialized = 1; } /* Test whether ARGV[woptind] points to a non-option argument. Either it does not have option syntax, or there is an environment flag from the shell indicating it is not an option. The later information is only used when the used in the GNU libc. */ # define NONOPTION_P (argv[woptind][0] != L'-' || argv[woptind][1] == L'\0') if (nextchar == NULL || *nextchar == '\0') { /* Advance to the next ARGV-element. */ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been moved back by the user (who may also have changed the arguments). */ if (last_nonopt > woptind) last_nonopt = woptind; if (first_nonopt > woptind) first_nonopt = woptind; if (ordering == PERMUTE) { /* If we have just processed some options following some non-options, exchange them so that the options come first. */ if (first_nonopt != last_nonopt && last_nonopt != woptind) exchange ((wchar_t **) argv); else if (last_nonopt != woptind) first_nonopt = woptind; /* Skip any additional non-options and extend the range of non-options previously skipped. */ while (woptind < argc && NONOPTION_P) woptind++; last_nonopt = woptind; } /* The special ARGV-element `--' means premature end of options. Skip it like a null option, then exchange with previous non-options as if it were an option, then skip everything else like a non-option. */ if (woptind != argc && !wcscmp (argv[woptind], L"--")) { woptind++; if (first_nonopt != last_nonopt && last_nonopt != woptind) exchange ((wchar_t **) argv); else if (first_nonopt == last_nonopt) first_nonopt = woptind; last_nonopt = argc; woptind = argc; } /* If we have done all the ARGV-elements, stop the scan and back over any non-options that we skipped and permuted. */ if (woptind == argc) { /* Set the next-arg-index to point at the non-options that we previously skipped, so the caller will digest them. */ if (first_nonopt != last_nonopt) woptind = first_nonopt; return -1; } /* If we have come to a non-option and did not permute it, either stop the scan or describe it to the caller and pass it by. */ if (NONOPTION_P) { if (ordering == REQUIRE_ORDER) return -1; woptarg = argv[woptind++]; return 1; } /* We have found another option-ARGV-element. Skip the initial punctuation. */ nextchar = (argv[woptind] + 1 + (longopts != NULL && argv[woptind][1] == L'-')); } /* Decode the current option-ARGV-element. */ /* Check whether the ARGV-element is a long option. If long_only and the ARGV-element has the form "-f", where f is a valid short option, don't consider it an abbreviated form of a long option that starts with f. Otherwise there would be no way to give the -f short option. On the other hand, if there's a long option "fubar" and the ARGV-element is "-fu", do consider that an abbreviation of the long option, just like "--fu", and not "-f" with arg "u". This distinction seems to be the most useful approach. */ if (longopts != NULL && (argv[woptind][1] == L'-' || (long_only && (argv[woptind][2] || !wcschr (optstring, argv[woptind][1]))))) { wchar_t *nameend; const struct woption *p; const struct woption *pfound = NULL; int exact = 0; int ambig = 0; int indfound = -1; int option_index; for (nameend = nextchar; *nameend && *nameend != L'='; nameend++) /* Do nothing. */ ; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!wcsncmp (p->name, nextchar, nameend - nextchar)) { if ((unsigned int) (nameend - nextchar) == (unsigned int) wcslen (p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (wopterr) fwprintf (stderr, L"%ls: option `%ls' is ambiguous\n", argv[0], argv[woptind]); nextchar += wcslen (nextchar); woptind++; woptopt = 0; return L'?'; } if (pfound != NULL) { option_index = indfound; woptind++; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) woptarg = nameend + 1; else { if (wopterr) { if (argv[woptind - 1][1] == L'-') /* --option */ fwprintf (stderr, L"%ls: option `--%ls' doesn't allow an argument\n", argv[0], pfound->name); else /* +option or -option */ fwprintf (stderr, L"%ls: option `%lc%ls' doesn't allow an argument\n", argv[0], argv[woptind - 1][0], pfound->name); } nextchar += wcslen (nextchar); woptopt = pfound->val; return L'?'; } } else if (pfound->has_arg == 1) { if (woptind < argc) woptarg = argv[woptind++]; else { if (wopterr) fwprintf (stderr, L"%ls: option `%ls' requires an argument\n", argv[0], argv[woptind - 1]); nextchar += wcslen (nextchar); woptopt = pfound->val; return optstring[0] == L':' ? L':' : L'?'; } } nextchar += wcslen (nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } /* Can't find it as a long option. If this is not getopt_long_only, or the option starts with '--' or is not a valid short option, then it's an error. Otherwise interpret it as a short option. */ if (!long_only || argv[woptind][1] == L'-' || wcschr (optstring, *nextchar) == NULL) { if (wopterr) { if (argv[woptind][1] == '-') /* --option */ fwprintf (stderr, L"%ls: unrecognized option `--%ls'\n", argv[0], nextchar); else /* +option or -option */ fwprintf (stderr, L"%ls: unrecognized option `%lc%ls'\n", argv[0], argv[woptind][0], nextchar); } nextchar = (wchar_t *) L""; woptind++; woptopt = 0; return L'?'; } } /* Look at and handle the next short option-character. */ { wchar_t c = *nextchar++; wchar_t *temp = wcschr (optstring, c); /* Increment `woptind' when we start to process its last character. */ if (*nextchar == L'\0') ++woptind; if (temp == NULL || c == L':') { if (wopterr) { if (posixly_correct) /* 1003.2 specifies the format of this message. */ fwprintf (stderr, L"%ls: illegal option -- %lc\n", argv[0], c); else fwprintf (stderr, L"%ls: invalid option -- %lc\n", argv[0], c); } woptopt = c; return L'?'; } /* Convenience. Treat POSIX -W foo same as long option --foo */ if (temp[0] == L'W' && temp[1] == L';') { wchar_t *nameend; const struct woption *p; const struct woption *pfound = NULL; int exact = 0; int ambig = 0; int indfound = 0; int option_index; /* This is an option that requires an argument. */ if (*nextchar != L'\0') { woptarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ woptind++; } else if (woptind == argc) { if (wopterr) { /* 1003.2 specifies the format of this message. */ fwprintf (stderr, L"%ls: option requires an argument -- %lc\n", argv[0], c); } woptopt = c; if (optstring[0] == L':') c = L':'; else c = L'?'; return c; } else /* We already incremented `woptind' once; increment it again when taking next ARGV-elt as argument. */ woptarg = argv[woptind++]; /* woptarg is now the argument, see if it's in the table of longopts. */ for (nextchar = nameend = woptarg; *nameend && *nameend != L'='; nameend++) /* Do nothing. */ ; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!wcsncmp (p->name, nextchar, nameend - nextchar)) { if ((unsigned int) (nameend - nextchar) == wcslen (p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (wopterr) fwprintf (stderr, L"%ls: option `-W %ls' is ambiguous\n", argv[0], argv[woptind]); nextchar += wcslen (nextchar); woptind++; return L'?'; } if (pfound != NULL) { option_index = indfound; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) woptarg = nameend + 1; else { if (wopterr) fwprintf (stderr, L"\ %ls: option `-W %ls' doesn't allow an argument\n", argv[0], pfound->name); nextchar += wcslen (nextchar); return L'?'; } } else if (pfound->has_arg == 1) { if (woptind < argc) woptarg = argv[woptind++]; else { if (wopterr) fwprintf (stderr, L"%ls: option `%ls' requires an argument\n", argv[0], argv[woptind - 1]); nextchar += wcslen (nextchar); return optstring[0] == L':' ? L':' : L'?'; } } nextchar += wcslen (nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } nextchar = NULL; return L'W'; /* Let the application handle it. */ } if (temp[1] == L':') { if (temp[2] == L':') { /* This is an option that accepts an argument optionally. */ if (*nextchar != L'\0') { woptarg = nextchar; woptind++; } else woptarg = NULL; nextchar = NULL; } else { /* This is an option that requires an argument. */ if (*nextchar != L'\0') { woptarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ woptind++; } else if (woptind == argc) { if (wopterr) { /* 1003.2 specifies the format of this message. */ fwprintf (stderr, L"%ls: option requires an argument -- %lc\n", argv[0], c); } woptopt = c; if (optstring[0] == L':') c = L':'; else c = L'?'; } else /* We already incremented `woptind' once; increment it again when taking next ARGV-elt as argument. */ woptarg = argv[woptind++]; nextchar = NULL; } } return c; } } int wgetopt (int argc, wchar_t *const *argv, const wchar_t *optstring) { return _wgetopt_internal (argc, argv, optstring, (const struct woption *) 0, (int *) 0, 0); } int wgetopt_long (int argc, wchar_t * const *argv, const wchar_t *options, const struct woption *long_options, int *opt_index) { return _wgetopt_internal (argc, argv, options, long_options, opt_index, 0); } /* Like getopt_long, but '-' as well as '--' can indicate a long option. If an option that starts with '-' (not '--') doesn't match a long option, but does match a short option, it is parsed as a short option instead. */ int wgetopt_long_only (int argc, wchar_t * const *argv, const wchar_t *options, const struct woption *long_options, int *opt_index) { return _wgetopt_internal (argc, argv, options, long_options, opt_index, 1); } wimlib-1.13.1/programs/wgetopt.h0000644000175000017500000000124012260745400013472 00000000000000#ifndef _WGETOPT_H #define _WGETOPT_H #include extern wchar_t *woptarg; extern int woptind, wopterr, woptopt; struct woption { const wchar_t *name; int has_arg; int *flag; int val; }; #define no_argument 0 #define required_argument 1 #define optional_argument 2 extern int wgetopt (int argc, wchar_t *const *argv, const wchar_t *optstring); extern int wgetopt_long(int argc, wchar_t * const *argv, const wchar_t *options, const struct woption *long_options, int *opt_index); extern int wgetopt_long_only(int argc, wchar_t *const *argv, const wchar_t *options, const struct woption *long_options, int *opt_index); #endif wimlib-1.13.1/programs/mkwinpeimg.in0000744000175000017500000003572413272231267014353 00000000000000#!/usr/bin/env bash # # This script can make a customized bootable image of Windows PE. # # Copyright (C) 2012, 2013 Eric Biggers # # 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 Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . script_name="$(basename "$0")" PREFIX_REG="::" WIMLIB_VERSION=@VERSION@ calc_columns () { STAT_COL=80 if [[ -t 0 ]]; then # stty will fail when stdin isn't a terminal STAT_COL=$(stty size) # stty gives "rows cols"; strip the rows number, we just want columns STAT_COL=${STAT_COL##* } elif tput cols &>/dev/null; then # is /usr/share/terminfo already mounted, and TERM recognized? STAT_COL=$(tput cols) fi if (( STAT_COL == 0 )); then # if output was 0 (serial console), set default width to 80 STAT_COL=80 fi # we use 13 characters for our own stuff STAT_COL=$(( STAT_COL - 13 )) if [[ -t 1 ]]; then SAVE_POSITION="\e[s" RESTORE_POSITION="\e[u" DEL_TEXT="\e[$(( STAT_COL + 4 ))G" else SAVE_POSITION="" RESTORE_POSITION="" DEL_TEXT="" fi } deltext() { printf "${DEL_TEXT}" } stat_busy() { printf "${PREFIX_REG} ${1} " printf "${SAVE_POSITION}" deltext printf " [BUSY] " } stat_done() { deltext printf " [DONE] \n" } stat_fail() { deltext printf " [FAIL] \n" exit 1 } cleanup() { rm -rf "$tmp_dir" } usage() { cat << EOF Usage: $script_name [OPTIONS] IMAGE -i, --iso Make an ISO image instead of a disk image. -o, --only-wim Make neither a disk image nor an ISO image; instead, only make a modified boot.wim file. -W, --windows-dir=DIR Use DIR as the location of the mounted Windows installation ISO image. If not specified, then the script tries the following locations: /mnt/windows, /mnt/windows7, /mnt/windows8, /mnt/windows10. -A, --waik-dir=DIR Get the boot files and boot.wim from the ISO image of the Windows Automated Installation Kit (WAIK) mounted on DIR instead of from a Windows installation ISO. This also works if the mounted ISO is for the WAIK supplement rather than for the WAIK itself. -s, --start-script=FILE Add FILE to the root directory of Windows PE image and adjust \Windows\System32\winpeshl.ini to execute FILE when Windows PE starts up. -w, --wim=WIM Use WIM as the boot.wim file. This defaults to the appropriate WIM file from the Windows or WAIK directory. -O, --overlay=DIR Adds all the files in DIR to the Windows PE image. -t, --tmp-dir=DIR Use DIR as the temporary base of the ISO filesystem. Defaults to making one using "mktemp -d". -a, --arch=ARCH Use the Windows PE version from the WAIK that has the CPU architecture ARCH. Possible values: "x86" or "amd64". Default is "x86". -h, --help Display this information. -v, --version Show version information. See \`man mkwinpeimg' for more information. EOF } version() { echo "$script_name (distributed with wimlib $WIMLIB_VERSION)" exit 0 } make=disk process_command_line() { if ! options=$(getopt -o oiw:W:s:O:t:A:a:hv -l \ only-wim,iso,wim:,windows-dir:,start-script:,overlay:,tmp-dir:,waik-dir:,arch:,help,version \ -- "$@" ); then usage exit 1 fi # default arch value arch="X86" arch_id="1" eval set -- "$options" while [ $# -gt 0 ]; do case "$1" in -i|--iso) make=iso ;; -o|--only-wim) make=wim ;; -W|--windows-dir) windows_dir="$2" windows_dir_specified=yes if [ -n "$waik_dir" ]; then echo 1>&2 "ERROR: Cannot specify both --windows-dir and --waik-dir!" exit 1 fi shift ;; -A|--waik-dir) waik_dir="$2" if [ -n "$windows_dir" ]; then echo 1>&2 "ERROR: Cannot specify both --windows-dir and --waik-dir!" exit 1 fi shift ;; -w|--wim) wim="$2" shift ;; -s|--start-script) start_script="$2" shift ;; -O|--overlay) overlay="$2" shift ;; -t|--tmp-dir) rmdir "$tmp_dir" tmp_dir="$2" shift ;; -a|--arch) if [ "$2" == "x86" ]; then arch="X86" arch_id="1" # Need to test Itanium images before making it an # option. Note: syslinux is x86 only so can't be used # for the Itanium disk image. #elif [ "$2" == "ia64" ]; then #arch="IA64" #arch_id="2" elif [ "$2" == "amd64" ]; then arch="AMD64" arch_id="3" else echo 1>&2 "ERROR: $2 is not a valid arch (x86/amd64)" exit 1 fi shift ;; -h|--help) usage exit 0 ;; -v|--version) version ;; --) shift break ;; *) echo 1>&2 "Invalid option \"$1\"" usage exit 1 ;; esac shift done if [ $# -ne 1 ]; then echo 1>&2 "You must specify the name of the image file to create!" echo 1>&2 "Run \"$script_name -h\" to see usage information." exit 1 else image="$1" fi } find_windows_dir() { if [ -z "$windows_dir_specified" ]; then for windows_dir in /mnt/windows /mnt/windows7 \ /mnt/windows8 /mnt/windows10; \ do if [ -d "$windows_dir"/sources ]; then break fi done fi if [ ! -d "$windows_dir" ]; then if [ -z "$windows_dir_specified" ]; then cat 1>&2 << EOF ERROR: Could not find the directory that the Windows (Vista or later) ISO image is mounted on! Please specify this directory using the --windows-dir option. EOF else echo 1>&2 "ERROR: Could not find the directory \"$windows_dir\"!" fi exit 1 fi if [ ! -d "$windows_dir/sources" ]; then cat 1>&2 << EOF ERROR: The directory "$windows_dir" exists, but it seems that a Windows (Vista or later) installation ISO image is not mounted on it. Please mount the image to continue. EOF exit 1 fi } check_needed_programs() { if [ -z "$waik_dir" -o -n "$modify_wim" ]; then if ! type -P wimlib-imagex &> /dev/null ; then cat 1>&2 << EOF ERROR: To make a customized image of Windows PE, we need the wimlib-imagex program from "wimlib" so that we can modify the boot.wim file. However, wimlib-imagex doesn't seem to be installed. Please install "wimlib" to continue. EOF exit 1 fi fi if [ $make = iso ]; then if ! type -P mkisofs &> /dev/null ; then cat 1>&2 << EOF ERROR: To make a bootable ISO image of Windows PE, we need the "mkisofs" program, but it doesn't seem to be installed. Please install the "cdrkit" package to continue, or try omitting the --iso option to make a disk image instead of an ISO image. EOF exit 1 fi elif [ $make = disk ] ; then if ! type -P syslinux &> /dev/null ; then cat 1>&2 << EOF ERROR: To make a bootable disk image of Windows PE, we need the "syslinux" program, but it doesn't seem to be installed. Please install the "syslinux" package to continue, or try using the --iso option to make an ISO image instead of a disk image. EOF exit 1 fi if ! type -P mformat mcopy &> /dev/null; then cat 1>&2 << EOF ERROR: To make a bootable disk image of Windows PE, we need the "mformat" and "mcopy" programs from the "mtools" package. These programs allow us to format a FAT filesystem and copy files to it without needing root privileges. Please install "mtools" if you want to make a disk image of Windows PE. Or, try using the --iso option to make an ISO image instead of a disk image. EOF fi fi if [ -n "$waik_dir" ] && [ -f "$waik_dir"/wAIK${arch}.msi ]; then if ! type -P cabextract &> /dev/null ; then cat 1>&2 << EOF ERROR: The boot files in the Windows Automated Installation Kit (WAIK) are inside cabinet archives. To extract these files, we need the "cabextract" program, but it doesn't seem to be installed. Please install "cabextract" to continue. EOF exit 1 fi fi } get_primary_boot_files() { if [ -n "$waik_dir" ]; then # Get boot files from the WAIK. stat_busy "Copying primary boot files from the Windows Automated Installation Kit ($waik_dir, $arch)" if [ -f "$waik_dir"/wAIK${arch}.msi ]; then if [ $make = iso ]; then cabextract "$waik_dir"/wAIK${arch}.msi -F F_WINPE_${arch}_etfsboot.com -p \ > "$tmp_dir"/etfsboot.com || stat_fail fi cabextract "$waik_dir"/wAIK${arch}.msi -F F${arch_id}_BOOTMGR -p \ > "$tmp_dir"/bootmgr || stat_fail cabextract "$waik_dir"/wAIK${arch}.msi -F F_WINPE_${arch}_boot.sdi -p \ > "$tmp_dir"/boot/boot.sdi || stat_fail cabextract "$waik_dir"/wAIK${arch}.msi -F F_WINPE_${arch}_bcd -p \ > "$tmp_dir"/boot/bcd || stat_fail # The WAIK supplement disc has a different structure else # Note: fuseiso, mount default to map=normal i.e. lowercase if [ $make = iso ]; then cp "$waik_dir"/${arch,,}/boot/etfsboot.com $tmp_dir/etfsboot.com || stat_fail fi cp "$waik_dir"/${arch,,}/bootmgr $tmp_dir/bootmgr || stat_fail cp "$waik_dir"/${arch,,}/boot/boot.sdi $tmp_dir/boot/boot.sdi || stat_fail cp "$waik_dir"/${arch,,}/boot/bcd $tmp_dir/boot/bcd || stat_fail fi stat_done else # Get boot files from the Windows ISO stat_busy "Copying primary boot files from mounted Windows ISO ($windows_dir)" if [ $make = iso ]; then cp "$windows_dir"/boot/etfsboot.com "$tmp_dir" || stat_fail fi cp "$windows_dir"/bootmgr "$tmp_dir" || stat_fail cp "$windows_dir"/boot/{bcd,boot.sdi} "$tmp_dir"/boot || stat_fail stat_done fi } get_boot_wim() { boot_wim="$1" # Copy the WIM over, or export the 2nd image in the WIM in the case of boot.wim # from the Windows ISO. remove_setup= if [ -z "$wim" ]; then # WIM file unspecified- grab it from the WAIK or the Windows ISO if [ -n "$waik_dir" ]; then # WAIK if [ -f "$waik_dir/WinPE.cab" ]; then stat_busy "Extracting boot.wim from \"$waik_dir/WinPE.cab\"" cabextract "$waik_dir/WinPE.cab" -F F${arch_id}_WINPE.WIM -p \ > "$boot_wim" 2>/dev/null || stat_fail # WAIK supplement has different layout else stat_busy "Copying boot.wim from \"${waik_dir}/${arch,,}/winpe.wim\"" cp "$waik_dir"/${arch,,}/winpe.wim "$boot_wim" || stat_fail chmod +w "$boot_wim" fi stat_done else # Windows ISO remove_setup=yes wim="$windows_dir/sources/boot.wim" stat_busy "Exporting image from \"$wim\"" wimlib-imagex export "$windows_dir"/sources/boot.wim 2 \ --boot "$boot_wim" || stat_fail stat_done fi else # WIM file specified stat_busy "Copying $wim to temporary directory" cp "$wim" "$boot_wim"|| stat_fail stat_done fi } # Make modifications to the WIM. modify_boot_wim() { boot_wim="$1" tmp_dir="$2" exec 3>"$tmp_dir/__mkwinpeimg.update.cmds" if [ -n "$remove_setup" ]; then stat_busy "Renaming setup.exe to prevent it from bothering us" cat 1>&3 <<- EOF rename /setup.exe /setup.exe.orig rename /sources/setup.exe /sources/setup.exe.orig EOF stat_done fi if [ -n "$start_script" ]; then stat_busy "Setting \"$start_script\" as the script to be executed when Windows PE boots" start_script_base="$(basename "$start_script")" cat > "$tmp_dir/__mkwinpeimg.winpeshl.ini" <<- EOF [LaunchApps] %SYSTEMDRIVE%\\$start_script_base EOF cat 1>&3 <<- EOF add '$start_script' '/$start_script_base' delete --force /Windows/System32/winpeshl.ini add '$tmp_dir/__mkwinpeimg.winpeshl.ini' /Windows/System32/winpeshl.ini EOF stat_done fi if [ -n "$overlay" ]; then stat_busy "Overlaying \"$overlay\" on the Windows PE filesystem" cat 1>&3 <<- EOF add '$overlay' / EOF stat_done fi exec 3>&- stat_busy "Rebuilding WIM with changes made" # Use case-insensitive mode; some Windows PE images contain a "windows" # directory instead of a "Windows" directory... WIMLIB_IMAGEX_IGNORE_CASE=1 wimlib-imagex update "$boot_wim" --rebuild \ < "$tmp_dir/__mkwinpeimg.update.cmds" > /dev/null || stat_fail stat_done } make_iso_img() { image="$1" # Make the ISO using the mkisofs command from cdrkit stat_busy "Making ISO image \"$image\"" mkisofs -sysid "" -A "" -V "Microsoft Windows PE ($arch)" -d -N \ -b etfsboot.com -no-emul-boot -c boot.cat -hide etfsboot.com \ -hide boot.cat -quiet -o "$image" "$tmp_dir" 1>&4 || stat_fail stat_done } make_disk_img() { image="$1" stat_busy "Making disk image \"$image\"" image_du=$(du -s -b "$tmp_dir" | cut -f 1) image_size=$(( image_du + 10000000 )) mtool_conf="$(mktemp)" dd if=/dev/zero of="$image" count=$(( (image_size + 4095) / 4096)) \ bs=4096 &> /dev/null cat > "$mtool_conf" <<- EOF MTOOLS_SKIP_CHECK=1 MTOOLS_FAT_COMPATIBILITY=1 drive s: file="$image" EOF export MTOOLSRC="$mtool_conf" mformat -h 255 -s 63 -T $(( image_size / 512)) s: || stat_fail mcopy -s "$tmp_dir"/* s: || stat_fail syslinux --install "$image" for biosdir in \ /usr/lib/syslinux/modules/bios \ /usr/lib/syslinux/bios \ /usr/lib/syslinux do if [ -e "$biosdir/chain.c32" ]; then break fi done mcopy "$biosdir/chain.c32" s: || stat_fail if [ -e "$biosdir/libcom32.c32" ]; then mcopy "$biosdir/libcom32.c32" s: fi if [ -e "$biosdir/libutil.c32" ]; then mcopy "$biosdir/libutil.c32" s: fi mcopy - 's:syslinux.cfg' <<- EOF DEFAULT winpe LABEL winpe COM32 chain.c32 APPEND ntldr=/bootmgr EOF rm -f "$mtool_conf" stat_done } calc_columns tmp_dir="$(mktemp -d)" process_command_line "$@" if [ "$image" = "-" ] ; then # Writing image to standard output if [ "$make" != iso ]; then echo 1>&2 "ERROR: Writing image to standard output is only supported in --iso mode!" exit 1 fi # We can't print anything to standard output except the ISO image # itself. Play with the file descriptors. exec 4>&1 # 4 is now the original standard output. exec 1>&2 # Anything that goes to standard output now, by default, # actually goes to standard error. else exec 4>&1 # 4 is now a copy of standard output fi if [ -z "$waik_dir" ]; then find_windows_dir fi if [ -n "$start_script" -o -n "$overlay" -o -n "$remove_setup" ]; then modify_wim=yes fi check_needed_programs trap cleanup EXIT if [ $make != wim ]; then mkdir -p "$tmp_dir"/{boot,sources} get_primary_boot_files fi if [ $make = wim ]; then boot_wim="$image" else boot_wim="$tmp_dir"/sources/boot.wim fi get_boot_wim "$boot_wim" if [ -n "$modify_wim" ]; then modify_boot_wim "$boot_wim" "$tmp_dir" fi if [ $make = iso ]; then make_iso_img "$image" elif [ $make = disk ]; then make_disk_img "$image" fi if [ "$image" != "-" ]; then echo "The image ($image) is $(stat -c %s "$image") bytes." fi wimlib-1.13.1/programs/mkwinpeimg0000744000175000017500000003572113464166632013751 00000000000000#!/usr/bin/env bash # # This script can make a customized bootable image of Windows PE. # # Copyright (C) 2012, 2013 Eric Biggers # # 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 Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . script_name="$(basename "$0")" PREFIX_REG="::" WIMLIB_VERSION=1.13.1 calc_columns () { STAT_COL=80 if [[ -t 0 ]]; then # stty will fail when stdin isn't a terminal STAT_COL=$(stty size) # stty gives "rows cols"; strip the rows number, we just want columns STAT_COL=${STAT_COL##* } elif tput cols &>/dev/null; then # is /usr/share/terminfo already mounted, and TERM recognized? STAT_COL=$(tput cols) fi if (( STAT_COL == 0 )); then # if output was 0 (serial console), set default width to 80 STAT_COL=80 fi # we use 13 characters for our own stuff STAT_COL=$(( STAT_COL - 13 )) if [[ -t 1 ]]; then SAVE_POSITION="\e[s" RESTORE_POSITION="\e[u" DEL_TEXT="\e[$(( STAT_COL + 4 ))G" else SAVE_POSITION="" RESTORE_POSITION="" DEL_TEXT="" fi } deltext() { printf "${DEL_TEXT}" } stat_busy() { printf "${PREFIX_REG} ${1} " printf "${SAVE_POSITION}" deltext printf " [BUSY] " } stat_done() { deltext printf " [DONE] \n" } stat_fail() { deltext printf " [FAIL] \n" exit 1 } cleanup() { rm -rf "$tmp_dir" } usage() { cat << EOF Usage: $script_name [OPTIONS] IMAGE -i, --iso Make an ISO image instead of a disk image. -o, --only-wim Make neither a disk image nor an ISO image; instead, only make a modified boot.wim file. -W, --windows-dir=DIR Use DIR as the location of the mounted Windows installation ISO image. If not specified, then the script tries the following locations: /mnt/windows, /mnt/windows7, /mnt/windows8, /mnt/windows10. -A, --waik-dir=DIR Get the boot files and boot.wim from the ISO image of the Windows Automated Installation Kit (WAIK) mounted on DIR instead of from a Windows installation ISO. This also works if the mounted ISO is for the WAIK supplement rather than for the WAIK itself. -s, --start-script=FILE Add FILE to the root directory of Windows PE image and adjust \Windows\System32\winpeshl.ini to execute FILE when Windows PE starts up. -w, --wim=WIM Use WIM as the boot.wim file. This defaults to the appropriate WIM file from the Windows or WAIK directory. -O, --overlay=DIR Adds all the files in DIR to the Windows PE image. -t, --tmp-dir=DIR Use DIR as the temporary base of the ISO filesystem. Defaults to making one using "mktemp -d". -a, --arch=ARCH Use the Windows PE version from the WAIK that has the CPU architecture ARCH. Possible values: "x86" or "amd64". Default is "x86". -h, --help Display this information. -v, --version Show version information. See \`man mkwinpeimg' for more information. EOF } version() { echo "$script_name (distributed with wimlib $WIMLIB_VERSION)" exit 0 } make=disk process_command_line() { if ! options=$(getopt -o oiw:W:s:O:t:A:a:hv -l \ only-wim,iso,wim:,windows-dir:,start-script:,overlay:,tmp-dir:,waik-dir:,arch:,help,version \ -- "$@" ); then usage exit 1 fi # default arch value arch="X86" arch_id="1" eval set -- "$options" while [ $# -gt 0 ]; do case "$1" in -i|--iso) make=iso ;; -o|--only-wim) make=wim ;; -W|--windows-dir) windows_dir="$2" windows_dir_specified=yes if [ -n "$waik_dir" ]; then echo 1>&2 "ERROR: Cannot specify both --windows-dir and --waik-dir!" exit 1 fi shift ;; -A|--waik-dir) waik_dir="$2" if [ -n "$windows_dir" ]; then echo 1>&2 "ERROR: Cannot specify both --windows-dir and --waik-dir!" exit 1 fi shift ;; -w|--wim) wim="$2" shift ;; -s|--start-script) start_script="$2" shift ;; -O|--overlay) overlay="$2" shift ;; -t|--tmp-dir) rmdir "$tmp_dir" tmp_dir="$2" shift ;; -a|--arch) if [ "$2" == "x86" ]; then arch="X86" arch_id="1" # Need to test Itanium images before making it an # option. Note: syslinux is x86 only so can't be used # for the Itanium disk image. #elif [ "$2" == "ia64" ]; then #arch="IA64" #arch_id="2" elif [ "$2" == "amd64" ]; then arch="AMD64" arch_id="3" else echo 1>&2 "ERROR: $2 is not a valid arch (x86/amd64)" exit 1 fi shift ;; -h|--help) usage exit 0 ;; -v|--version) version ;; --) shift break ;; *) echo 1>&2 "Invalid option \"$1\"" usage exit 1 ;; esac shift done if [ $# -ne 1 ]; then echo 1>&2 "You must specify the name of the image file to create!" echo 1>&2 "Run \"$script_name -h\" to see usage information." exit 1 else image="$1" fi } find_windows_dir() { if [ -z "$windows_dir_specified" ]; then for windows_dir in /mnt/windows /mnt/windows7 \ /mnt/windows8 /mnt/windows10; \ do if [ -d "$windows_dir"/sources ]; then break fi done fi if [ ! -d "$windows_dir" ]; then if [ -z "$windows_dir_specified" ]; then cat 1>&2 << EOF ERROR: Could not find the directory that the Windows (Vista or later) ISO image is mounted on! Please specify this directory using the --windows-dir option. EOF else echo 1>&2 "ERROR: Could not find the directory \"$windows_dir\"!" fi exit 1 fi if [ ! -d "$windows_dir/sources" ]; then cat 1>&2 << EOF ERROR: The directory "$windows_dir" exists, but it seems that a Windows (Vista or later) installation ISO image is not mounted on it. Please mount the image to continue. EOF exit 1 fi } check_needed_programs() { if [ -z "$waik_dir" -o -n "$modify_wim" ]; then if ! type -P wimlib-imagex &> /dev/null ; then cat 1>&2 << EOF ERROR: To make a customized image of Windows PE, we need the wimlib-imagex program from "wimlib" so that we can modify the boot.wim file. However, wimlib-imagex doesn't seem to be installed. Please install "wimlib" to continue. EOF exit 1 fi fi if [ $make = iso ]; then if ! type -P mkisofs &> /dev/null ; then cat 1>&2 << EOF ERROR: To make a bootable ISO image of Windows PE, we need the "mkisofs" program, but it doesn't seem to be installed. Please install the "cdrkit" package to continue, or try omitting the --iso option to make a disk image instead of an ISO image. EOF exit 1 fi elif [ $make = disk ] ; then if ! type -P syslinux &> /dev/null ; then cat 1>&2 << EOF ERROR: To make a bootable disk image of Windows PE, we need the "syslinux" program, but it doesn't seem to be installed. Please install the "syslinux" package to continue, or try using the --iso option to make an ISO image instead of a disk image. EOF exit 1 fi if ! type -P mformat mcopy &> /dev/null; then cat 1>&2 << EOF ERROR: To make a bootable disk image of Windows PE, we need the "mformat" and "mcopy" programs from the "mtools" package. These programs allow us to format a FAT filesystem and copy files to it without needing root privileges. Please install "mtools" if you want to make a disk image of Windows PE. Or, try using the --iso option to make an ISO image instead of a disk image. EOF fi fi if [ -n "$waik_dir" ] && [ -f "$waik_dir"/wAIK${arch}.msi ]; then if ! type -P cabextract &> /dev/null ; then cat 1>&2 << EOF ERROR: The boot files in the Windows Automated Installation Kit (WAIK) are inside cabinet archives. To extract these files, we need the "cabextract" program, but it doesn't seem to be installed. Please install "cabextract" to continue. EOF exit 1 fi fi } get_primary_boot_files() { if [ -n "$waik_dir" ]; then # Get boot files from the WAIK. stat_busy "Copying primary boot files from the Windows Automated Installation Kit ($waik_dir, $arch)" if [ -f "$waik_dir"/wAIK${arch}.msi ]; then if [ $make = iso ]; then cabextract "$waik_dir"/wAIK${arch}.msi -F F_WINPE_${arch}_etfsboot.com -p \ > "$tmp_dir"/etfsboot.com || stat_fail fi cabextract "$waik_dir"/wAIK${arch}.msi -F F${arch_id}_BOOTMGR -p \ > "$tmp_dir"/bootmgr || stat_fail cabextract "$waik_dir"/wAIK${arch}.msi -F F_WINPE_${arch}_boot.sdi -p \ > "$tmp_dir"/boot/boot.sdi || stat_fail cabextract "$waik_dir"/wAIK${arch}.msi -F F_WINPE_${arch}_bcd -p \ > "$tmp_dir"/boot/bcd || stat_fail # The WAIK supplement disc has a different structure else # Note: fuseiso, mount default to map=normal i.e. lowercase if [ $make = iso ]; then cp "$waik_dir"/${arch,,}/boot/etfsboot.com $tmp_dir/etfsboot.com || stat_fail fi cp "$waik_dir"/${arch,,}/bootmgr $tmp_dir/bootmgr || stat_fail cp "$waik_dir"/${arch,,}/boot/boot.sdi $tmp_dir/boot/boot.sdi || stat_fail cp "$waik_dir"/${arch,,}/boot/bcd $tmp_dir/boot/bcd || stat_fail fi stat_done else # Get boot files from the Windows ISO stat_busy "Copying primary boot files from mounted Windows ISO ($windows_dir)" if [ $make = iso ]; then cp "$windows_dir"/boot/etfsboot.com "$tmp_dir" || stat_fail fi cp "$windows_dir"/bootmgr "$tmp_dir" || stat_fail cp "$windows_dir"/boot/{bcd,boot.sdi} "$tmp_dir"/boot || stat_fail stat_done fi } get_boot_wim() { boot_wim="$1" # Copy the WIM over, or export the 2nd image in the WIM in the case of boot.wim # from the Windows ISO. remove_setup= if [ -z "$wim" ]; then # WIM file unspecified- grab it from the WAIK or the Windows ISO if [ -n "$waik_dir" ]; then # WAIK if [ -f "$waik_dir/WinPE.cab" ]; then stat_busy "Extracting boot.wim from \"$waik_dir/WinPE.cab\"" cabextract "$waik_dir/WinPE.cab" -F F${arch_id}_WINPE.WIM -p \ > "$boot_wim" 2>/dev/null || stat_fail # WAIK supplement has different layout else stat_busy "Copying boot.wim from \"${waik_dir}/${arch,,}/winpe.wim\"" cp "$waik_dir"/${arch,,}/winpe.wim "$boot_wim" || stat_fail chmod +w "$boot_wim" fi stat_done else # Windows ISO remove_setup=yes wim="$windows_dir/sources/boot.wim" stat_busy "Exporting image from \"$wim\"" wimlib-imagex export "$windows_dir"/sources/boot.wim 2 \ --boot "$boot_wim" || stat_fail stat_done fi else # WIM file specified stat_busy "Copying $wim to temporary directory" cp "$wim" "$boot_wim"|| stat_fail stat_done fi } # Make modifications to the WIM. modify_boot_wim() { boot_wim="$1" tmp_dir="$2" exec 3>"$tmp_dir/__mkwinpeimg.update.cmds" if [ -n "$remove_setup" ]; then stat_busy "Renaming setup.exe to prevent it from bothering us" cat 1>&3 <<- EOF rename /setup.exe /setup.exe.orig rename /sources/setup.exe /sources/setup.exe.orig EOF stat_done fi if [ -n "$start_script" ]; then stat_busy "Setting \"$start_script\" as the script to be executed when Windows PE boots" start_script_base="$(basename "$start_script")" cat > "$tmp_dir/__mkwinpeimg.winpeshl.ini" <<- EOF [LaunchApps] %SYSTEMDRIVE%\\$start_script_base EOF cat 1>&3 <<- EOF add '$start_script' '/$start_script_base' delete --force /Windows/System32/winpeshl.ini add '$tmp_dir/__mkwinpeimg.winpeshl.ini' /Windows/System32/winpeshl.ini EOF stat_done fi if [ -n "$overlay" ]; then stat_busy "Overlaying \"$overlay\" on the Windows PE filesystem" cat 1>&3 <<- EOF add '$overlay' / EOF stat_done fi exec 3>&- stat_busy "Rebuilding WIM with changes made" # Use case-insensitive mode; some Windows PE images contain a "windows" # directory instead of a "Windows" directory... WIMLIB_IMAGEX_IGNORE_CASE=1 wimlib-imagex update "$boot_wim" --rebuild \ < "$tmp_dir/__mkwinpeimg.update.cmds" > /dev/null || stat_fail stat_done } make_iso_img() { image="$1" # Make the ISO using the mkisofs command from cdrkit stat_busy "Making ISO image \"$image\"" mkisofs -sysid "" -A "" -V "Microsoft Windows PE ($arch)" -d -N \ -b etfsboot.com -no-emul-boot -c boot.cat -hide etfsboot.com \ -hide boot.cat -quiet -o "$image" "$tmp_dir" 1>&4 || stat_fail stat_done } make_disk_img() { image="$1" stat_busy "Making disk image \"$image\"" image_du=$(du -s -b "$tmp_dir" | cut -f 1) image_size=$(( image_du + 10000000 )) mtool_conf="$(mktemp)" dd if=/dev/zero of="$image" count=$(( (image_size + 4095) / 4096)) \ bs=4096 &> /dev/null cat > "$mtool_conf" <<- EOF MTOOLS_SKIP_CHECK=1 MTOOLS_FAT_COMPATIBILITY=1 drive s: file="$image" EOF export MTOOLSRC="$mtool_conf" mformat -h 255 -s 63 -T $(( image_size / 512)) s: || stat_fail mcopy -s "$tmp_dir"/* s: || stat_fail syslinux --install "$image" for biosdir in \ /usr/lib/syslinux/modules/bios \ /usr/lib/syslinux/bios \ /usr/lib/syslinux do if [ -e "$biosdir/chain.c32" ]; then break fi done mcopy "$biosdir/chain.c32" s: || stat_fail if [ -e "$biosdir/libcom32.c32" ]; then mcopy "$biosdir/libcom32.c32" s: fi if [ -e "$biosdir/libutil.c32" ]; then mcopy "$biosdir/libutil.c32" s: fi mcopy - 's:syslinux.cfg' <<- EOF DEFAULT winpe LABEL winpe COM32 chain.c32 APPEND ntldr=/bootmgr EOF rm -f "$mtool_conf" stat_done } calc_columns tmp_dir="$(mktemp -d)" process_command_line "$@" if [ "$image" = "-" ] ; then # Writing image to standard output if [ "$make" != iso ]; then echo 1>&2 "ERROR: Writing image to standard output is only supported in --iso mode!" exit 1 fi # We can't print anything to standard output except the ISO image # itself. Play with the file descriptors. exec 4>&1 # 4 is now the original standard output. exec 1>&2 # Anything that goes to standard output now, by default, # actually goes to standard error. else exec 4>&1 # 4 is now a copy of standard output fi if [ -z "$waik_dir" ]; then find_windows_dir fi if [ -n "$start_script" -o -n "$overlay" -o -n "$remove_setup" ]; then modify_wim=yes fi check_needed_programs trap cleanup EXIT if [ $make != wim ]; then mkdir -p "$tmp_dir"/{boot,sources} get_primary_boot_files fi if [ $make = wim ]; then boot_wim="$image" else boot_wim="$tmp_dir"/sources/boot.wim fi get_boot_wim "$boot_wim" if [ -n "$modify_wim" ]; then modify_boot_wim "$boot_wim" "$tmp_dir" fi if [ $make = iso ]; then make_iso_img "$image" elif [ $make = disk ]; then make_disk_img "$image" fi if [ "$image" != "-" ]; then echo "The image ($image) is $(stat -c %s "$image") bytes." fi wimlib-1.13.1/programs/imagex-win32.c0000644000175000017500000000427013016523676014225 00000000000000/* Windows-specific code for wimlib-imagex. */ #ifndef __WIN32__ # error "This file contains Windows code" #endif #include "imagex-win32.h" #include #include #include #include /* Convert a string from the "current Windows codepage" to UTF-16LE. */ wchar_t * win32_mbs_to_wcs(const char *mbs, size_t mbs_nbytes, size_t *num_wchars_ret) { if (mbs_nbytes > INT_MAX) { fwprintf(stderr, L"ERROR: too much data (%zu bytes)!\n", mbs_nbytes); return NULL; } if (mbs_nbytes == 0) { *num_wchars_ret = 0; return (wchar_t*)mbs; } int len = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, mbs, mbs_nbytes, NULL, 0); if (len <= 0) goto out_invalid; wchar_t *wcs = malloc(len * sizeof(wchar_t)); if (!wcs) { fwprintf(stderr, L"ERROR: out of memory!\n"); return NULL; } int len2 = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, mbs, mbs_nbytes, wcs, len); if (len2 != len) { free(wcs); goto out_invalid; } *num_wchars_ret = len; return wcs; out_invalid: fwprintf(stderr, L"ERROR: Invalid multi-byte string in the text file you provided as input!\n" L" Maybe try converting your text file to UTF-16LE?\n" ); return NULL; } /* Set a file descriptor to binary mode. */ void set_fd_to_binary_mode(int fd) { _setmode(fd, _O_BINARY); } #include static wchar_t * get_security_descriptor_string(PSECURITY_DESCRIPTOR desc) { wchar_t *str = NULL; /* 52 characters!!! */ ConvertSecurityDescriptorToStringSecurityDescriptorW( desc, SDDL_REVISION_1, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, &str, NULL); return str; } void win32_print_security_descriptor(const uint8_t *sd, size_t size) { wchar_t *str; const wchar_t *printstr; /* 'size' is ignored here due to the crappy Windows APIs. Oh well, this * is just for debugging anyway. */ str = get_security_descriptor_string((PSECURITY_DESCRIPTOR)sd); if (str) printstr = str; else printstr = L"(invalid)"; wprintf(L"Security Descriptor = %ls\n", printstr); LocalFree(str); } wimlib-1.13.1/programs/imagex-win32.h0000644000175000017500000000114013016523676014223 00000000000000#ifndef _IMAGEX_WIN32_H #define _IMAGEX_WIN32_H #include #include #include #include extern wchar_t * win32_mbs_to_wcs(const char *mbs, size_t mbs_nbytes, size_t *num_wchars_ret); extern void win32_print_security_descriptor(const uint8_t *sd, size_t size); extern void set_fd_to_binary_mode(int fd); #include "wgetopt.h" #define optarg woptarg #define optind woptind #define opterr wopterr #define optopt woptopt #define option woption #define getopt_long_only wgetopt_long_only #define getopt_long wgetopt_long #define getopt wgetopt #endif wimlib-1.13.1/wimlib.pc.in0000644000175000017500000000055113160354225012217 00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: wimlib Description: Library to extract, create, modify, and mount Windows Imaging (WIM) files URL: https://wimlib.net Version: @VERSION@ Libs: -L${libdir} -lwim Requires.private: @PKGCONFIG_PRIVATE_REQUIRES@ Libs.private: @PKGCONFIG_PRIVATE_LIBS@ Cflags: -I${includedir} wimlib-1.13.1/rpm/0000755000175000017500000000000013464166215010667 500000000000000wimlib-1.13.1/rpm/wimtools.spec0000644000175000017500000000631213464166215013342 00000000000000Name: wimtools Summary: Tools to create, extract, modify, and mount WIM files Version: 1.13.1 Release: 1 License: GPLv3+ URL: https://wimlib.net Packager: Eric Biggers Source: https://wimlib.net/downloads/wimlib-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root Group: Applications/System Requires: libwim15 %description Tools to extract, create, modify, and mount WIM (Windows Imaging) files. WIM is an archive format designed primarily for archiving Windows filesystems. It features single-instancing and LZ77-based compression and is used by Microsoft to distribute and deploy Windows Vista and later. WIM files are normally created by using the `imagex.exe' utility on Windows, but this package contains a free implementation of ImageX called "wimlib-imagex" that is designed to work on both UNIX-like systems and Windows. In addition to the usual extract/create/update support, wimlib-imagex allows you to mount WIM images readonly or read-write, and it even allows you to extract or create a WIM image directly to/from an unmounted NTFS volume. This makes it possible to, from Linux, back up or deploy a Windows OS directly to or from a WIM file, such as the install.wim distributed on the Windows installation media. This package also contains a script to make a customized Windows PE image based on the capabilities provided by wimlib-imagex. %package -n libwim15-devel Summary: Development files for wimlib Group: Development/Libraries %description -n libwim15-devel Development files for wimlib %package -n libwim15 Summary: Library to extract, create, modify, and mount WIM files Group: System Environment/Libraries Requires: fuse BuildRequires: libxml2-devel, fuse, fuse-devel, openssl-devel, attr BuildRequires: ntfs-3g-devel, ntfsprogs, libtool, pkgconfig %description -n libwim15 wimlib is a C library for extracting, creating, modifying, and mounting WIM (Windows Imaging) files. WIM is an archive format designed primarily for archiving Windows filesystems. It features single-instancing and LZ77-based compression, and is used by Microsoft to distribute and deploy Windows Vista and later. wimlib is an independent implementation of an API for handling WIM files, available on both UNIX-like systems and Windows, that provides features similar to Microsoft's WIMGAPI, as well as additional features such as support for pipable WIM files and programatically making changes to WIM images without mounting them. %post -n libwim15 -p /sbin/ldconfig %postun -n libwim15 -p /sbin/ldconfig %prep %setup -q -n wimlib-%{version} %build %configure --prefix=/usr \ --disable-rpath \ --with-libcrypto \ --with-ntfs-3g \ --with-fuse make %{?_smp_mflags} %install rm -rf %{buildroot} make DESTDIR=%{buildroot} install %clean rm -rf %{buildroot} %files %defattr(-, root, root) %{_bindir}/* %doc %{_mandir}/man1/*.1.gz %doc README COPYING COPYING.GPLv3 %files -n libwim15-devel %defattr(-, root, root) %{_libdir}/libwim.a %{_libdir}/libwim.so %exclude %{_libdir}/libwim.la %{_includedir}/wimlib.h %{_libdir}/pkgconfig/wimlib.pc %files -n libwim15 %defattr(-, root, root) %{_libdir}/libwim.so.* %doc COPYING COPYING.GPLv3 COPYING.LGPLv3 COPYING.CC0 wimlib-1.13.1/tests/0000755000175000017500000000000013464166632011236 500000000000000wimlib-1.13.1/tests/exclusionlists/0000755000175000017500000000000013324657474014332 500000000000000wimlib-1.13.1/tests/exclusionlists/multiple_stars0000644000175000017500000000014413324657474017243 00000000000000[ExclusionList] foo*bar*baz @@@ bar barbaz baz foo foobar - foobarbaz - fooXYZbarXYZbaz wimlib-1.13.1/tests/exclusionlists/suffix_match0000644000175000017500000000011313324657474016650 00000000000000[ExclusionList] *.jpg @@@ a.png - a.jpg dir/ dir/a.png - dir/a.jpg wimlib-1.13.1/tests/exclusionlists/anchored_exception_in_excluded_dir0000644000175000017500000000015213324657474023235 00000000000000[ExclusionList] /dir [ExclusionException] /dir/include* @@@ dir/ - dir/exclude.txt dir/include.txt wimlib-1.13.1/tests/exclusionlists/anchored_simple0000644000175000017500000000011013324657474017321 00000000000000[ExclusionList] /foo @@@ - foo foobar goo/ goo/foo goo/foobar wimlib-1.13.1/tests/exclusionlists/case_sensitive0000644000175000017500000000004613324657474017201 00000000000000[ExclusionList] foo @@@ - foo FOO wimlib-1.13.1/tests/exclusionlists/prefix_match0000644000175000017500000000012713324657474016646 00000000000000[ExclusionList] foo* @@@ boo boofoo - foo - foobar goo/ - goo/foo - goo/foobar wimlib-1.13.1/tests/exclusionlists/recursive_match0000644000175000017500000000014713324657474017362 00000000000000[ExclusionList] dir @@@ - dir/ - dir/a - dir/b foo goo subdir/ - subdir/dir/ - subdir/dir/abcd wimlib-1.13.1/tests/exclusionlists/case_insensitive0000644000175000017500000000010513324657474017524 00000000000000# case insensitive [ExclusionList] foo @@@ - FOO - foo BOO boo wimlib-1.13.1/tests/exclusionlists/anchored_prefix_match0000644000175000017500000000011113324657474020502 00000000000000[ExclusionList] /foo* @@@ - foo - foobar goo/ goo/foo goo/foobar wimlib-1.13.1/tests/exclusionlists/simple_exception0000644000175000017500000000014413324657474017543 00000000000000[ExclusionList] *.jpg [ExclusionException] test.jpg @@@ - a.jpg - abcd.jpg abcd.png test.jpg wimlib-1.13.1/tests/exclusionlists/inner_star0000644000175000017500000000007713324657474016345 00000000000000[ExclusionList] foo*bar @@@ bar foo - foobar - fooXYZbar wimlib-1.13.1/tests/exclusionlists/wildcard_exception0000644000175000017500000000015313324657474020043 00000000000000[ExclusionList] *.jpg [ExclusionException] ?oo.jpg @@@ - a.jpg - abcd.jpg boo.jpg foo.jpg foo.png wimlib-1.13.1/tests/exclusionlists/question_mark0000644000175000017500000000011113324657474017047 00000000000000[ExclusionList] te?t @@@ - test - text - te?t best testing Xtest wimlib-1.13.1/tests/security_descriptor_1.bin0000644000175000017500000000032012260745400016155 00000000000000 ¸Ä¤ÿÿ ÿ© ! ! !wimlib-1.13.1/tests/test-imagex-update_and_extract0000744000175000017500000001571713324700147017166 00000000000000#!/usr/bin/env bash # Test `wimupdate' and `wimextract'. set -e cd tests srcdir="${srcdir:-.}/.." srcdir="$(cd $srcdir; pwd)" . "$srcdir/tests/test_utils.sh" TEST_SUBDIR=tmpdir_test-imagex-update_and_extract default_cleanup mkdir $TEST_SUBDIR cd $TEST_SUBDIR msg() { echo "--------------------------------------------------------------------" echo $1 echo "--------------------------------------------------------------------" } fail() { msg "TEST FAILED (See above)" } trap fail EXIT prepare_empty_wim() { rm -rf in.dir mkdir in.dir wimcapture in.dir test.wim --compress=none } do_apply() { rm -rf out.dir wimapply test.wim out.dir } prepare_empty_wim cp $srcdir/src/add_image.c file echo 1 > 1 echo 2 > 2 msg "Testing deleting nonexisting file from WIM image, without --force (errors expected)" ! wimupdate test.wim << EOF delete /nonexistent EOF msg "Testing deleting nonexisting file from WIM image, with --force" ! wimupdate test.wim << EOF delete --force /nonexistent EOF msg "Testing deleting root directory from WIM image, without --recursive (errors expected)" ! wimupdate test.wim << EOF delete / EOF msg "Testing deleting root directory from WIM image, with --recursive" wimupdate test.wim << EOF delete --recursive / EOF msg "Testing update command with invalid option (errors expected)" ! wimupdate test.wim << EOF delete --invalid-option --recursive / EOF msg "Testing update command with too many arguments (errors expected)" ! wimupdate test.wim << EOF delete --recursive --force / /anotherdir EOF msg "Testing invalid update command (errors expected)" ! wimupdate test.wim << EOF invalid / EOF msg "Testing update command file with comments and empty lines" wimupdate test.wim << EOF # this is a comment # comment # add # delete # rename EOF msg "Testing update with --rebuild" wimupdate --rebuild test.wim < /dev/null for flag in "" "--rebuild"; do msg "Testing adding file to WIM image with flag \"$flag\"" wimupdate test.wim $flag << EOF add file /file EOF do_apply ../tree-cmp file out.dir/file msg "Testing deleting file from WIM image" wimupdate test.wim << EOF delete /file EOF do_apply [ ! -e out.dir/file ] done msg "Testing renaming file in WIM image" wimupdate test.wim << EOF add file /file EOF wimupdate test.wim << EOF rename file newname EOF do_apply ../tree-cmp file out.dir/newname [ ! -e out.dir/file ] prepare_empty_wim msg "Testing adding, then renaming file in WIM image in one command" wimupdate test.wim << EOF add file /file rename /file /newname EOF do_apply ../tree-cmp file out.dir/newname [ ! -e out.dir/file ] msg "Testing adding additional file to WIM image" prepare_empty_wim wimupdate test.wim << EOF add 1 /1 EOF wimupdate test.wim << EOF add file /file EOF do_apply [ -e out.dir/1 ] [ -e out.dir/file ] msg "Testing extracting file from WIM image" rm -rf out.dir mkdir out.dir wimextract test.wim 1 /file --dest-dir=out.dir ../tree-cmp file out.dir/file [ ! -e out.dir/1 ] msg "Testing extracting file from WIM image to stdout" rm -rf out.dir mkdir out.dir wimlib_imagex extract test.wim 1 /file --to-stdout > out.dir/file cmp file out.dir/file [ ! -e out.dir/1 ] msg "Testing adding directories and files to WIM image" rm -rf dir1 mkdir dir1 rm -rf dir2 mkdir dir2 echo 5 > dir1/5 echo 6 > dir2/6.1 echo 6 > dir2/6 echo 6 > dir2/6.2 ln -s 5 dir1/relink mkdir dir1/subdir ln dir1/5 dir1/5link ln dir2/6 dir2/6link prepare_empty_wim wimupdate test.wim 1 << EOF add dir1 /dir1 add dir2 /prefix/dir2 EOF rm -rf out.dir mkdir out.dir wimextract test.wim 1 dir1 --dest-dir=out.dir wimextract test.wim 1 prefix/dir2 --dest-dir=out.dir ../tree-cmp dir1 out.dir/dir1 ../tree-cmp dir2 out.dir/dir2 msg "Testing adding files to WIM image" rm -rf in.dir mkdir in.dir wimappend in.dir test.wim "2" cp $srcdir/src/*.c in.dir wimupdate test.wim 2 << EOF add in.dir / add file /file EOF cp -a file in.dir/file rm -rf out.dir wimapply test.wim 2 out.dir touch -r in.dir out.dir ../tree-cmp in.dir out.dir msg "Testing adding file with space in it" echo hello > "Some File" prepare_empty_wim wimupdate test.wim 1 << EOF add "Some File" 'Some Destination' EOF rm -rf out.dir wimapply test.wim 1 out.dir ../tree-cmp "Some File" out.dir/"Some Destination" msg "Testing path list extract" echo hello1 > hello1 echo hello2 > hello2 echo otherfile > otherfile prepare_empty_wim wimupdate test.wim 1 << EOF add hello1 /hello1 add hello2 /hello2 add otherfile /otherfile EOF cat > pathlist << EOF hello1 hello2 EOF rm -rf out.dir wimextract test.wim 1 @pathlist --dest-dir=out.dir ../tree-cmp hello1 out.dir/hello1 ../tree-cmp hello2 out.dir/hello2 [ ! -e out.dir/otherfile ] msg "Testing path list extract (stdin)" rm -rf out.dir wimextract test.wim 1 @- --dest-dir=out.dir << EOF hello1 hello2 EOF ../tree-cmp hello1 out.dir/hello1 ../tree-cmp hello2 out.dir/hello2 [ ! -e out.dir/otherfile ] msg "Testing path list extract (w/ wildcard)" cat > pathlist << EOF hello* EOF rm -rf out.dir wimextract test.wim 1 @pathlist --dest-dir=out.dir ../tree-cmp hello1 out.dir/hello1 ../tree-cmp hello2 out.dir/hello2 [ ! -e out.dir/otherfile ] cat > pathlist << EOF hello* EOF rm -rf out.dir msg "Testing path list extract (no wildcard, no match; error expected)" ! wimextract test.wim 1 @pathlist --dest-dir=out.dir --no-wildcards cat > pathlist << EOF foobar* EOF rm -rf out.dir msg "Testing path list extract (wildcard, no match; error expected)" ! wimextract test.wim 1 @pathlist --dest-dir=out.dir msg "Testing path list extract (wildcard, no match, nullglob; no error expected)" wimextract test.wim 1 @pathlist --dest-dir=out.dir --nullglob msg "Testing path list extract (w/ wildcard)" cat > pathlist << EOF * EOF rm -rf out.dir wimextract test.wim 1 @pathlist --dest-dir=out.dir ../tree-cmp hello1 out.dir/hello1 ../tree-cmp hello2 out.dir/hello2 ../tree-cmp otherfile out.dir/otherfile msg "Testing path list extract (subdir files)" prepare_empty_wim wimupdate test.wim 1 << EOF add hello1 /topdir/subdir1/hello1 add hello2 /topdir/subdir2/hello2 add hello1 /topdir/hello1 EOF cat > pathlist << EOF /topdir/subdir?/hello* EOF rm -rf out.dir wimextract test.wim 1 @pathlist --dest-dir=out.dir ../tree-cmp hello1 out.dir/topdir/subdir1/hello1 ../tree-cmp hello2 out.dir/topdir/subdir2/hello2 [ ! -e out.dir/topdir/hello1 ] msg "Testing case insensitivity" prepare_empty_wim wimupdate test.wim 1 << EOF add hello1 /HELLO1 EOF cat > pathlist << EOF hello1 EOF rm -rf out.dir ! WIMLIB_IMAGEX_IGNORE_CASE=0 wimextract test.wim 1 @pathlist --dest-dir=out.dir ! WIMLIB_IMAGEX_IGNORE_CASE=0 wimextract test.wim 1 @pathlist --dest-dir=out.dir --no-wildcards WIMLIB_IMAGEX_IGNORE_CASE=1 wimextract test.wim 1 @pathlist --dest-dir=out.dir WIMLIB_IMAGEX_IGNORE_CASE=1 wimextract test.wim 1 @pathlist --dest-dir=out.dir --no-wildcards ../tree-cmp hello1 out.dir/HELLO1 [ ! -e out.dir/topdir/hello1 ] echo "**********************************************************" echo " wimupdate/extract tests passed " echo "**********************************************************" trap EXIT cd .. default_cleanup wimlib-1.13.1/tests/tree-cmp.c0000644000175000017500000002636613272231267013045 00000000000000/* * A program to compare directory trees * * There are two modes: * - Normal mode for any filesystems. We compare file names, contents, * sizes, modes, access times, and hard links. * - NTFS mode for NTFS-3G mounted volumes. In this mode we need to * compare various NTFS-specific attributes such as named data streams * and DOS names. * * Both modes compare hard link groups between the two directory trees. If two * files are hard linked together in one directory tree, exactly the same two * files are expected to be hard linked together in the other directory tree. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_XATTR_H # include #endif #include #ifndef ENOATTR # define ENOATTR ENODATA #endif typedef uint64_t u64; #if 0 # define DEBUG(format, ...) \ ({ \ int __errno_save = errno; \ fprintf(stdout, "[%s %d] %s(): " format, \ __FILE__, __LINE__, __func__, ## __VA_ARGS__); \ putchar('\n'); \ fflush(stdout); \ errno = __errno_save; \ }) #else #define DEBUG(format, ...) #endif static bool ntfs_mode = false; static void difference(const char *format, ...) { va_list va; va_start(va, format); fflush(stdout); fputs("tree-cmp: ", stderr); vfprintf(stderr, format, va); putc('\n', stderr); fflush(stderr); va_end(va); exit(1); } static void error(const char *format, ...) { va_list va; int err = errno; va_start(va, format); fflush(stdout); fputs("tree-cmp: ", stderr); vfprintf(stderr, format, va); fprintf(stderr, ": %s\n", strerror(err)); va_end(va); exit(2); } /* This is just a binary tree that maps inode numbers in one NTFS tree to inode * numbers in the other NTFS tree. This is so we can tell if the hard link * groups are the same between the two NTFS trees. */ struct node { u64 ino_from; u64 ino_to; struct node *left; struct node *right; }; static struct node *tree = NULL; static const char *root1, *root2; static u64 do_lookup_ino(struct node *tree, u64 ino_from) { if (!tree) return -1; if (ino_from == tree->ino_from) return tree->ino_to; else if (ino_from < tree->ino_from) return do_lookup_ino(tree->left, ino_from); else return do_lookup_ino(tree->right, ino_from); } static void do_insert(struct node *tree, struct node *node) { if (node->ino_from < tree->ino_from) { if (tree->left) return do_insert(tree->left, node); else tree->left = node; } else { if (tree->right) return do_insert(tree->right, node); else tree->right = node; } } static u64 lookup_ino(u64 ino_from) { return do_lookup_ino(tree, ino_from); } static void insert_ino(u64 ino_from, u64 ino_to) { struct node *node = malloc(sizeof(struct node)); if (!node) error("Out of memory"); node->ino_from = ino_from; node->ino_to = ino_to; node->left = NULL; node->right = NULL; if (!tree) tree = node; else do_insert(tree, node); } /* Compares the "normal" contents of two files of size @size. */ static void cmp(const char *file1, const char *file2, size_t size) { int fd1, fd2; char buf1[4096], buf2[4096]; ssize_t to_read = 4096; fd1 = open(file1, O_RDONLY); if (fd1 == -1) error("Could not open `%s'", file1); fd2 = open(file2, O_RDONLY); if (fd2 == -1) error("Could not open `%s'", file2); for (; size; size -= to_read) { if (to_read > size) to_read = size; if (read(fd1, buf1, to_read) != to_read) error("Error reading `%s'", file1); if (read(fd2, buf2, to_read) != to_read) error("Error reading `%s'", file2); if (memcmp(buf1, buf2, to_read)) difference("File contents of `%s' and `%s' differ", file1, file2); } close(fd1); close(fd2); } #ifdef HAVE_SYS_XATTR_H /* Compares an extended attribute of the files. */ static void cmp_xattr(const char *file1, const char *file2, const char *xattr_name, ssize_t max_size, bool missingok) { ssize_t len1, len2; char *buf1, *buf2; DEBUG("cmp xattr \"%s\" of files %s, %s", xattr_name, file1, file2); len1 = lgetxattr(file1, xattr_name, NULL, 0); if (len1 == -1) { if (errno == ENOATTR) { if (missingok) { errno = 0; lgetxattr(file2, xattr_name, NULL, 0); if (errno == ENOATTR) return; else difference("xattr `%s' exists on file `%s' " "but not on file `%s'", xattr_name, file1, file2); } else { error("Could not find attribute `%s' of `%s'", xattr_name, file1); } } else { error("Could not read xattr `%s' of `%s'", xattr_name, file1); } } buf1 = malloc(len1); buf2 = malloc(len1); if (!buf1 || !buf2) error("Out of memory"); if (lgetxattr(file1, xattr_name, buf1, len1) != len1) error("Could not read xattr `%s' of `%s'", xattr_name, file1); len2 = lgetxattr(file2, xattr_name, buf2, len1); if (len2 == len1) { if (memcmp(buf1, buf2, (max_size == 0 || len1 <= max_size) ? len1 : max_size)) { difference("xattr `%s' of files `%s' and `%s' differs", xattr_name, file1, file2); } } else { if (len2 == -1) { error("Could not read xattr `%s' from `%s'", xattr_name, file2); } if (len1 != len2) difference("xattr `%s' of files `%s' and `%s' differs", xattr_name, file1, file2); } free(buf1); free(buf2); } /* Compares all alternate data streams of the files */ static void cmp_ads(const char *file1, const char *file2) { char _list1[256], _list2[sizeof(_list1)]; char *list1 = _list1, *list2 = _list2; char *pe, *p; ssize_t len1, len2, tmp; errno = 0; len1 = llistxattr(file1, list1, sizeof(_list1)); if (len1 == -1) { if (errno != ERANGE || ((len1 = llistxattr(file1, NULL, 0) == -1))) error("Could not get xattr list of `%s'", file1); list1 = malloc(len1); list2 = malloc(len1); if (!list1 || !list2) error("Out of memory"); tmp = llistxattr(file1, list1, len1); if (tmp == -1) error("Could not get xattr list of `%s'", file1); if (tmp != len1) error("xattr list of `%s' changed as we read it", file1); } errno = 0; len2 = llistxattr(file2, list2, len1); if (len1 == -1) { if (errno == ERANGE) difference("`%s' and `%s' do not have the same " "xattr list", file1, file2); else error("Could not get xattr list of `%s'", file2); } if (len1 != len2 || memcmp(list1, list2, len1)) difference("`%s' and `%s' do not have the same " "xattr list", file1, file2); p = list1; pe = list1 + len1 - 1; while (p < pe) { cmp_xattr(file1, file2, p, 0, false); p += strlen(p) + 1; } if (list1 != _list1) { free(list1); free(list2); } } #endif /* HAVE_SYS_XATTR_H */ /* Compares special NTFS data of the files, as accessed through extended * attributes. */ static void special_cmp(const char *file1, const char *file2) { #ifdef HAVE_SYS_XATTR_H cmp_xattr(file1, file2, "system.ntfs_acl", 0, false); cmp_xattr(file1, file2, "system.ntfs_attrib", 0, false); cmp_xattr(file1, file2, "system.ntfs_dos_name", 0, true); cmp_xattr(file1, file2, "system.ntfs_object_id", 64, true); cmp_xattr(file1, file2, "system.ntfs_reparse_data", 0, true); cmp_xattr(file1, file2, "system.ntfs_times", 16, false); cmp_ads(file1, file2); #else fprintf(stderr, "tree-cmp: Warning: cannot compare xattrs of `%s' and `%s'\n", file1, file2); fprintf(stderr, " Extended attributes are not supported on this platform.\n"); #endif } /* Recursively compares directory tree rooted at file1 to directory tree rooted at file2 */ static void tree_cmp(char file1[], int file1_len, char file2[], int file2_len) { struct stat st1, st2; u64 ino_from, ino_to; DEBUG("cmp files %s, %s", file1, file2); if (lstat(file1, &st1)) error("Failed to stat `%s'", file1); if (lstat(file2, &st2)) error("Failed to stat `%s'", file2); ino_from = st1.st_ino; ino_to = lookup_ino(ino_from); if (ino_to == -1) insert_ino(ino_from, st2.st_ino); else if (ino_to != st2.st_ino) difference("Inode number on `%s' is wrong", file2); if ((st1.st_mode & ~(S_IRWXU | S_IRWXG | S_IRWXO)) != (st2.st_mode & ~(S_IRWXU | S_IRWXG | S_IRWXO))) difference("Modes of `%s' and `%s' are not the same", file1, file2); if (S_ISREG(st1.st_mode) && st1.st_size != st2.st_size) difference("Sizes of `%s' and `%s' are not the same", file1, file2); #if 0 if (ntfs_mode && st1.st_atime != st2.st_atime) difference("Access times of `%s' (%x) and `%s' (%x) are " "not the same", file1, st1.st_atime, file2, st2.st_atime); #endif if (st1.st_mtime != st2.st_mtime) difference("Modification times of `%s' (%x) and `%s' (%x) are " "not the same", file1, st1.st_mtime, file2, st2.st_mtime); if ((ntfs_mode || S_ISREG(st1.st_mode)) && st1.st_nlink != st2.st_nlink) difference("Link count of `%s' (%u) and `%s' (%u) " "are not the same", file1, st1.st_nlink, file2, st2.st_nlink); if (ntfs_mode && strcmp(file1, root1) != 0) special_cmp(file1, file2); if (S_ISREG(st1.st_mode)) cmp(file1, file2, st1.st_size); else if (S_ISDIR(st1.st_mode)) { int ret1, ret2; int i; struct dirent **namelist1, **namelist2; const char *dir1 = file1, *dir2 = file2; ret1 = scandir(dir1, &namelist1, NULL, alphasort); if (ret1 == -1) error("Error scanning directory `%s'", dir1); ret2 = scandir(dir2, &namelist2, NULL, alphasort); if (ret2 == -1) error("Error scanning directory `%s'", dir2); if (ret1 != ret2) difference("Directories `%s' and `%s' do not contain " "the same number of entries", dir1, dir2); file1[file1_len] = '/'; file2[file2_len] = '/'; for (i = 0; i < ret1; i++) { int name_len; const char *name; if (strcmp(namelist1[i]->d_name, namelist2[i]->d_name)) { difference("Files `%s' and `%s' in directories " "`%s' and `%s', respectively, do " "not have the same name", namelist1[i]->d_name, namelist2[i]->d_name, dir1, dir2); } name = namelist1[i]->d_name; name_len = strlen(name); if (!(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))) { memcpy(file1 + file1_len + 1, name, name_len + 1); memcpy(file2 + file2_len + 1, name, name_len + 1); tree_cmp(file1, file1_len + 1 + name_len, file2, file2_len + 1 + name_len); } free(namelist1[i]); free(namelist2[i]); } free(namelist1); free(namelist2); file1[file1_len] = '\0'; file2[file2_len] = '\0'; } else if (!ntfs_mode && S_ISLNK(st1.st_mode)) { char buf1[4096], buf2[sizeof(buf1)]; ssize_t ret1, ret2; ret1 = readlink(file1, buf1, sizeof(buf1)); if (ret1 == -1) error("Failed to get symlink target of `%s'", file1); ret2 = readlink(file2, buf2, sizeof(buf2)); if (ret2 == -1) error("Failed to get symlink target of `%s'", file2); if (ret1 != ret2 || memcmp(buf1, buf2, ret1)) error("Symlink targets of `%s' and `%s' differ", file1, file2); } } int main(int argc, char **argv) { if (argc != 3 && argc != 4) { fprintf(stderr, "Usage: %s DIR1 DIR2 [NTFS]", argv[0]); return 2; } if (argc > 3 && strcmp(argv[3], "NTFS") == 0) ntfs_mode = true; char dir1[4096]; char dir2[4096]; strcpy(dir1, argv[1]); strcpy(dir2, argv[2]); root1 = argv[1]; root2 = argv[2]; tree_cmp(dir1, strlen(dir1), dir2, strlen(dir2)); return 0; } wimlib-1.13.1/tests/security_descriptor_2.base640000644000175000017500000000027412260745400016502 00000000000000AQAEoHQAAACAAAAAAAAAABQAAAACAGAABAAAAAAAFAD/AR8AAQEAAAAAAAUSAAAAABAYAP8BHwABAgAAAAAABSAAAAAgAgAAABAUAP8BHwABAQAAAAAABRIAAAAAEBgAqQASAAECAAAAAAAFIAAAACECAAABAQAAAAAABRIAAAABAQAAAAAABRIAAAA=wimlib-1.13.1/tests/test_utils.sh0000644000175000017500000000315013160354225013676 00000000000000if stat -c %i . &> /dev/null ; then BSD_STAT=0 else BSD_STAT=1 fi get_inode_number() { if [ "$BSD_STAT" -eq 1 ]; then stat -f %i "$1" else stat -c %i "$1" fi } get_link_count() { if [ "$BSD_STAT" -eq 1 ]; then stat -f %l "$1" else stat -c %h "$1" fi } get_file_size() { if [ "$BSD_STAT" -eq 1 ]; then stat -f %z "$1" else stat -c %s "$1" fi } wimlib_imagex() { ../../wimlib-imagex "$@" } wimappend() { wimlib_imagex append "$@" > /dev/null } wimapply() { wimlib_imagex apply "$@" > /dev/null } wimcapture() { wimlib_imagex capture "$@" > /dev/null } wimdelete() { wimlib_imagex delete "$@" > /dev/null } wimdir() { wimlib_imagex dir "$@" } wimexport() { wimlib_imagex export "$@" > /dev/null } wimextract() { wimlib_imagex extract "$@" > /dev/null } wiminfo() { wimlib_imagex info "$@" } wimjoin() { wimlib_imagex join "$@" > /dev/null } wimmount() { wimlib_imagex mount "$@" > /dev/null } wimmountrw() { wimlib_imagex mountrw "$@" > /dev/null } wimoptimize() { wimlib_imagex optimize "$@" > /dev/null } wimsplit() { wimlib_imagex split "$@" > /dev/null } wimunmount() { wimlib_imagex unmount "$@" > /dev/null } wimupdate() { wimlib_imagex update "$@" > /dev/null } wimverify() { wimlib_imagex verify "$@" > /dev/null } wim_ctype() { wiminfo $1 | grep Compression | awk '{print $2}' } default_cleanup() { rm -rf $TEST_SUBDIR } error() { echo "****************************************************************" echo " Test failure " echo $* echo "****************************************************************" exit 1 } wimlib-1.13.1/tests/test-imagex-mount0000744000175000017500000001771213160354225014467 00000000000000#!/usr/bin/env bash # Test WIM mounting set -e cd tests srcdir="${srcdir:-.}/.." srcdir="$(cd $srcdir; pwd)" . "$srcdir/tests/test_utils.sh" TEST_SUBDIR=tmpdir_test-imagex-mount skip_test() { cd .. cleanup exit 77 } imagex_unmount() { # Give the --lazy flag to work around a problem testing on Ubuntu and # other OS's running Gnome, as they have a daemon # "gvfs-gdu-volume-monitor" that apparently likes to randomly read # filesystems that get mounted, thereby stopping them from being # unmounted. wimunmount "$@" --lazy } cleanup() { fusermount -u $TEST_SUBDIR/tmp.mnt &> /dev/null || true rm -rf $TEST_SUBDIR } init() { mkdir dir cp $srcdir/src/*.c dir mkdir dir/subdir echo 'hello' > dir/subdir/hello echo 'hello' > dir/subdir/hello2 ln dir/subdir/hello dir/subdir/hellolink echo -n > dir/subdir/empty_file ln -s hello dir/subdir/rel_symlink mkdir dir2 echo 'testing' > dir2/file dd if=/dev/zero of=dir2/zeroes bs=4096 count=5 mkdir tmp.empty tmp.mnt tmp.apply tmp.orig wimcapture tmp.empty empty.wim --norpfix } if [ ! -r /dev/fuse ] || [ ! -w /dev/fuse ]; then echo "WARNING: /dev/fuse is not readable and writable." echo "Skipping WIM mounting checks" skip_test fi cleanup mkdir $TEST_SUBDIR cd $TEST_SUBDIR init # wimmount for flag in "--compress=none" "--compress=maximum" "--compress=fast"; do echo "Using flag $flag" echo "Testing mounting WIM read-only" if ! wimcapture dir dir.wim $flag; then error "Failed to capture WIM" fi if ! wimmount dir.wim dir tmp.mnt; then error "Failed to mount test WIM read-only. " \ "Please read any error messages above before reporting this test failure. "\ "Perhaps you don't have FUSE installed, or the FUSE kernel module isn't" \ "loaded, or you aren't a member of the FUSE group?" fi echo "Testing extracting file from mounted read-only WIM" if ! cp tmp.mnt/write.c write.c; then error "Failed to extract file from read-only mounted WIM" fi if ! diff -q dir/write.c write.c; then error "Extracted file does not match copy in mounted WIM" fi if ! diff -q tmp.mnt/write.c dir/write.c; then error "Extractef file does not match original" fi rm -f write.c echo "Testing modifying mounted read-only WIM (should fail)" if rm tmp.mnt/write.c; then error "Removing file from read-only mounted WIM didn't fail" fi if touch tmp.mnt/newfile; then error "Creating file on read-only mounted WIM didn't fail" fi if echo 3 > tmp.mnt/write.c; then error "Writing to file on read-only mounted WIM didn't fail" fi echo "Testing diff of mounted read-only WIM with original directory" if ! diff -q -r tmp.mnt dir; then error "Recursive diff of read-only mounted WIM with original directory failed" fi echo "Testing unmount of read-only filesystem" if ! imagex_unmount tmp.mnt; then error "Unmounting read-only WIM failed" fi echo "Testing unmount of read-only filesystem with --commit given" if ! wimmount dir.wim dir tmp.mnt; then error "Failed to re-mount WIM read-only" fi if ! imagex_unmount tmp.mnt --commit; then error "Failed to unmount read-only WIM with --commit flag (should be ignored)" fi rm -rf dir.wim done # wimmountrw echo "Testing mounting WIM read-write" if ! wimcapture dir dir.wim; then error "Failed to capture WIM" fi if ! wimmountrw dir.wim dir tmp.mnt; then error "Failed to mount test WIM read-write" fi echo "Testing unmounting WIM unmodified" if ! imagex_unmount tmp.mnt; then error "Failed to unmount test WIM unmodified" fi echo "Testing unmounting WIM unmodified with --commit and --check" if ! wimmountrw dir.wim dir tmp.mnt; then error "Failed to re-mount test WIM read-write" fi if ! imagex_unmount tmp.mnt --commit --check; then error "Failed to unmount read-write mounted WIM with changes commited (no changes made)" fi echo "Testing removing file from mounted WIM" if ! wimmountrw dir.wim dir tmp.mnt; then error "Failed to re-mount test WIM read-write" fi if ! rm tmp.mnt/write.c; then error "Failed to remove file from read-write mounted WIM" fi if test -f tmp.mnt/write.c; then error "Removing file from read-write mounted WIM failed" fi echo "Testing making directory in mounted WIM" if ! mkdir tmp.mnt/newdir; then error "Failed to make directory in read-write mounted WIM" fi if ! test -d tmp.mnt/newdir; then error "Making directory in read-write mounted WIM failed" fi echo "Testing making new empty file in mounted WIM" if ! touch tmp.mnt/newdir/empty_file; then error "Could not create new empty file in read-write mounted WIM" fi if ! test -f tmp.mnt/newdir/empty_file; then error "New empty file not created correctly in read-write mounted WIM" fi if ! test "`get_file_size tmp.mnt/newdir/empty_file`" = 0; then error "New empty file in read-write mounted WIM is not empty" fi echo "Testing making new non-empty file in mounted WIM" if ! dd if=/dev/zero of=tmp.mnt/newdir/zeroes1 bs=1 count=4096; then error "Failed to make new non-empty file in mounted WIM" fi if ! dd if=/dev/zero of=tmp.mnt/newdir/zeroes2 bs=4096 count=1; then error "Failed to make new non-empty file in mounted WIM" fi if ! diff -q tmp.mnt/newdir/zeroes1 tmp.mnt/newdir/zeroes2; then error "New files in mounted WIM not made correctly" fi echo "Unmounting WIM with changes committed and --check" if ! imagex_unmount tmp.mnt --commit --check; then error "Failed to unmount read-write mounted WIM" fi if ! wiminfo dir.wim | grep -q Integrity; then error "Integrity information was not included" fi if ! wimapply dir.wim tmp.apply; then error "Failed to apply WIM we had previously mounted read-write" fi if ! diff -q tmp.apply/newdir/zeroes1 tmp.apply/newdir/zeroes2; then error "The new non-empty files we made in the read-write mounted WIM were not extracted correctly" fi if test `get_file_size tmp.apply/newdir/empty_file` != 0; then error "The new empty file we made in the read-write mounted WIM was not extracted correctly" fi if test `get_file_size tmp.apply/newdir/zeroes1` != 4096; then error "The new non-empty files we made in the read-write mounted WIM were not extracted correctly" fi rm -rf tmp.apply/* # Now do some tests using tar. do_tree_cmp() { if ! ../tree-cmp $1 $2; then if [ -x /usr/bin/tree ]; then echo "Dumping tree of applied image" tree $2 --inodes -F -s --noreport error 'Information was lost or corrupted while capturing and then applying a directory tree' fi fi } msg() { echo "--------------------------------------------------------------------" echo "Testing making $1 on read-write mounted WIM" echo "--------------------------------------------------------------------" } do_test() { # Create tree, tar it up, and untar it on an empty WIM image mounted # read-write cp empty.wim test.wim cd tmp.orig eval "$1" if [ -x /usr/bin/tree ]; then tree . --inodes -F -s --noreport fi tar cf ../test.tar . cd .. if ! wimmountrw test.wim tmp.mnt --unix-data; then error "Failed to mount WIM read-write" fi cd tmp.mnt if ! tar xf ../test.tar; then error "Failed to untar archive on read-write mounted WIM" fi cd .. # Diff the original tree with the mounted WIM do_tree_cmp tmp.orig tmp.mnt # Clear the mounted WIM and do it again! (We need to test deleting # stuff as well as creating stuff.) if ! rm -rf tmp.mnt/*; then error "Failed to clear mounted WIM" fi cd tmp.mnt if ! tar xf ../test.tar; then error "Failed to untar archive on read-write mounted WIM" fi cd .. # Diff the original tree with the mounted WIM do_tree_cmp tmp.orig tmp.mnt # Unmount the WIM, apply it, and diff the original tree with the applied # tree if ! imagex_unmount tmp.mnt --commit; then error "Failed to unmount WIM mounted read-write" fi if ! wimapply test.wim tmp.apply; then error "Failed to apply WIM we previously had mounted read-write" fi do_tree_cmp tmp.orig tmp.apply rm -rf tmp.orig/* tmp.apply/* } . $srcdir/tests/common_tests.sh echo "**********************************************************" echo " WIM mount tests passed " echo "**********************************************************" cd .. cleanup wimlib-1.13.1/tests/test-imagex-capture_and_apply0000744000175000017500000002224013324665616017022 00000000000000#!/usr/bin/env bash # Test capturing and applying a WIM image in the normal (non-NTFS) capture mode # # Add in some tests with WIM splitting, joining, and exporting as well. # # Test all three compression modes (None, XPRESS, and LZX). # # Also, test if the capture configuration file works correctly. set -e cd tests srcdir="${srcdir:-.}/.." srcdir="$(cd $srcdir; pwd)" . "$srcdir/tests/test_utils.sh" TEST_SUBDIR=tmpdir_test-imagex-capture_and_apply do_tree_cmp() { if ! ../tree-cmp in.dir out.dir; then if [ -x /usr/bin/tree ]; then echo "Dumping tree of applied image" echo "(Note: compression type was $ctype)" tree out.dir --inodes -F -s --noreport error 'Information was lost or corrupted while capturing and then applying a directory tree' fi fi } image_name=0 do_test() { for ctype in None XPRESS LZX; do # Can we capture the WIM, apply it, and get the same result? cd in.dir eval "$1" cd .. if [ -x /usr/bin/tree -a "$ctype" = "None" ]; then tree in.dir --inodes -F -s --noreport fi if ! wimcapture in.dir test.wim --compress=$ctype --norpfix; then error "Failed to capture directory tree into a WIM" fi if ! wimapply test.wim 1 out.dir; then error "Failed to apply WIM to directory" fi if [ `wim_ctype test.wim` != $ctype ]; then error "'wiminfo' didn't report the compression type on the captured WIM correctly" fi do_tree_cmp rm -rf out.dir/* # Can we split the WIM, apply the split WIM, join the split WIM, # and apply the joined WIM, and get the same results every time? # # LC_ALL=C avoids locale-dependent floating point number # parsing. if ! LC_ALL=C wimsplit test.wim test.swm 0.01; then error "Failed to split WIM" fi if ! wimapply test.swm 1 out.dir --ref "test*.swm" ; then error "Failed to apply split WIM" fi do_tree_cmp rm -rf out.dir/* test.wim if ! wimjoin test.wim test*.swm; then error "Failed to join split WIM" fi if ! wimapply test.wim out.dir; then error "Failed to apply joined WIM" fi do_tree_cmp rm -rf out.dir/* # Can we export the image to another WIM, apply it, and get the # same results? (( image_name++ )) || true if ! wimexport test.wim 1 test2.wim "$image_name"; then error "Failed to export WIM image" fi if ! wimapply test2.wim "$image_name" out.dir; then error "Failed to apply exported WIM image" fi do_tree_cmp rm -rf out.dir/* # Try pipable WIM (don't bother testing all compression types # though, it shouldn't make a difference). if [ "$ctype" = "None" ]; then # Capture pipable WIM (not writing to pipe) if ! wimcapture in.dir test.wim \ --compress=$ctype --norpfix --pipable; then error "Failed to capture directory tree into a pipable WIM" fi # Apply pipable WIM (reading from pipe) if ! cat test.wim | wimapply - 1 out.dir; then error "Failed to apply pipable WIM to directory (from pipe)" fi do_tree_cmp rm -rf out.dir/* # Apply pipable WIM (not reading from pipe) if ! wimapply test.wim 1 out.dir; then error "Failed to apply pipable WIM to directory (not from pipe)" fi do_tree_cmp rm -rf out.dir/* # Capture pipable WIM (writing to pipe) and read pipable # WIM (reading from pipe) if ! wimlib_imagex capture --pipable --compress=$ctype \ --norpfix --pipable \ in.dir - | wimapply - 1 out.dir; then error "Failed to capture directory tree into a pipable WIM" fi do_tree_cmp rm -rf out.dir/* fi rm -rf out.dir/* in.dir/* test.wim test*.swm done } __msg() { echo "--------------------------------------------------------------------" echo $1 echo "--------------------------------------------------------------------" } msg() { __msg "Testing image capture and application of directory containing $1" } default_cleanup mkdir $TEST_SUBDIR cd $TEST_SUBDIR mkdir in.dir out.dir . $srcdir/tests/common_tests.sh # Make sure exclusion list works __msg "Testing default capture configuration file" touch in.dir/hiberfil.sys mkdir -p "in.dir/System Volume Information/subdir" wimcapture in.dir test.wim wimapply test.wim out.dir if [ -e out.dir/hiberfil.sys -o -e "out.dir/System Volume Information" ]; then error "Files were not excluded from capture as expected" fi # # Execute one of the exclusion list test cases in tests/exclusionlists/. # The file is divided into two sections, separated by a line containing "@@@". # The first is the capture configuration file. The second is a list of files # and directories, where the ones that should be excluded are marked with "- ". # exclusionlist_test() { local t_file="$1" local in_paths_section=false local path local include __msg "Testing exclusion list: ${t_file##*/}" echo "/" > expected_out sed -n -e '/^@@@/q;p' "$t_file" > config.txt rm -rf in.dir mkdir in.dir cd in.dir while read -r path; do path="$(echo "$path" | sed -e 's/[[:space:]]*$//')" if ! $in_paths_section; then if [ "$path" = "# case insensitive" ]; then export WIMLIB_IMAGEX_IGNORE_CASE=1 elif [ "$path" = "@@@" ]; then in_paths_section=true fi continue fi if [ -z "$path" ]; then continue fi include=true if [ "${path:0:2}" = "- " ]; then include=false path="${path:2}" fi if [ "${path: -1}" = "/" ]; then path="${path:0:$(( ${#path} - 1))}" mkdir "$path" else touch "$path" fi if $include; then echo "/$path" >> ../expected_out fi done < "$t_file" cd .. $in_paths_section || error "malformed test file: $t_file (never found separator)" wimcapture in.dir test.wim --compress=none --config=config.txt unset WIMLIB_IMAGEX_IGNORE_CASE wimdir test.wim 1 > actual_out diff expected_out actual_out } for t_file in "$srcdir/tests/exclusionlists"/*; do exclusionlist_test "$t_file" done # Make sure reparse point fixups are working as expected __msg "Testing --rpfix" rm -r in.dir out.dir mkdir in.dir ln -s $PWD/in.dir in.dir/absrootlink ln -s $PWD/in.dir//// in.dir/absrootlinkslashes ln -s /___NONEXISTENT___ in.dir/absnonexistent ln -s /usr/bin/env in.dir/absoutoftree ln -s file in.dir/relalink ln -s $PWD/in.dir/file in.dir/abslink ln -s $PWD/in.dir/file/// in.dir/abslinkslashes wimcapture --rpfix in.dir test.wim wimapply --norpfix test.wim out.dir if [[ `readlink out.dir/absrootlink` != "/" ]] || [[ `readlink out.dir/absrootlinkslashes` != "////" ]]; then error "wimcapture --rpfix failed to fix absolute link to capture root" fi if [[ ! -L out.dir/absnonexistent ]] || [[ ! -L out.dir/absoutoftree ]]; then error "wimcapture --rpfix failed to also capture out of tree absolute links" fi if [[ `readlink out.dir/relalink` != "file" ]]; then error "wimcapture --rpfix failed to capture relative symlink" fi if [[ `readlink out.dir/abslink` != "/file" ]] || [[ `readlink out.dir/abslinkslashes` != "/file///" ]]; then error "wimcapture --rpfix did fix absolute link properly" fi rm -rf out.dir wimapply test.wim out.dir if [[ $(get_inode_number $(readlink out.dir/absrootlink)) != \ $(get_inode_number out.dir) ]]; then error "wimapply failed to apply fixed absolute symlinks" fi # Make sure source list mode is working as expected __msg "Testing source list capture mode" rm -rf in.dir out.dir mkdir in.dir echo 1 > in.dir/1 ln in.dir/1 in.dir/1link echo 5 > 5 mkdir otherdir cp $srcdir/src/add_image.c otherdir cat > srclist << EOF in.dir / 5 /5 otherdir /otherdir EOF wimcapture srclist --source-list test.wim wimapply test.wim out.dir if [[ ! -f out.dir/5 || ! -f out.dir/1 || ! -f out.dir/1link || \ ! -d out.dir/otherdir ]]; then error "source list capture failed to work as expected" fi # Still testing source list capture: add quoted name, and try overlay rm -rf out.dir cat > srclist << EOF in.dir / 5 /5 otherdir /otherdir "overlay dir 1" 'otherdir' "overlay dir 2" 'otherdir' EOF mkdir "overlay dir 1" mkdir "overlay dir 2" echo A > "overlay dir 1"/A echo B > "overlay dir 2"/B wimcapture srclist --source-list test.wim wimapply test.wim out.dir if [[ ! -f out.dir/5 || ! -f out.dir/1 || ! -f out.dir/1link || \ ! -f out.dir/otherdir/A || ! -f out.dir/otherdir/B ]]; then error "source list capture (with quoted names and basic overlay) failed to work as expected" fi # Try deep overlay rm -rf in.dir out.dir "overlay dir 1" "overlay dir 2" mkdir -p in.dir.1/subdir/subdir2 in.dir.2/subdir/subdir2 cat > srclist << EOF in.dir.1 / in.dir.2 / EOF echo 1 > in.dir.1/subdir/1 echo 2 > in.dir.2/subdir/2 echo 3 > in.dir.1/subdir/subdir2/3 echo 4 > in.dir.2/subdir/subdir2/4 wimcapture srclist --source-list test.wim wimapply test.wim out.dir if [[ ! -f out.dir/subdir/1 || ! -f out.dir/subdir/2 || \ ! -f out.dir/subdir/subdir2/3 || ! -f out.dir/subdir/subdir2/4 ]]; then error "source list capture (with deep overlay) failed to work as expected" fi # Try bad overlay __msg "Testing bad overlay (errors expected)" rm -rf out.dir echo 5 > 5 cat > srclist << EOF in.dir.1 / in.dir.2 / 5 /subdir EOF if wimcapture srclist --source-list test.wim; then error "unexpected success in bad overlay with --source-list!" fi echo "**********************************************************" echo " wimcapture/apply tests passed " echo "**********************************************************" cd .. default_cleanup wimlib-1.13.1/tests/security_descriptor_2.bin0000644000175000017500000000021412260745400016160 00000000000000 t€`ÿÿ ÿ© !wimlib-1.13.1/tests/security_descriptor_1.base640000644000175000017500000000043012260745400016473 00000000000000AQAEoLgAAADEAAAAAAAAABQAAAACAKQABwAAAAAAFAD/AR8AAQEAAAAAAAUSAAAAABMYAP8BHwABAgAAAAAABSAAAAAgAgAAABMUAP8BHwABAQAAAAAABRIAAAAAGxQAAAAAEAEBAAAAAAADAAAAAAATGACpABIAAQIAAAAAAAUgAAAAIQIAAAASGAAEAAAAAQIAAAAAAAUgAAAAIQIAAAASGAACAAAAAQIAAAAAAAUgAAAAIQIAAAEBAAAAAAAFEgAAAAEBAAAAAAAFEgAAAA==wimlib-1.13.1/tests/common_tests.sh0000644000175000017500000000353513272231267014224 00000000000000 msg "nothing" do_test "" msg "a single file" do_test "echo 1 > file" msg "a single directory" do_test "mkdir dir" msg "subdirectory with file" do_test "mkdir dir; echo 1 > dir/file" msg "empty file" do_test "echo -n > empty_file" msg "two empty files" do_test "echo -n > empty_file_1; echo -n > empty_file_2" msg "hard link in same directory" do_test "echo 1 > file; ln file link" msg "hard link between empty files" do_test "echo -n > empty_file; ln empty_file link" msg "relative symbolic link" do_test "echo 1 > file; ln -s file symlink" msg "absolute symbolic link" do_test "echo 1 > file; ln -s /some/absolute/target symlink" msg "large file" do_test "dd if=/dev/zero of=file bs=4096 count=10 &> /dev/null" msg "many nested directories" do_test 'mkdir dir; mkdir dir/subdir; mkdir dir/subdir/subdir2; mkdir dir/subdir/subdir3' msg "identical files and symlinks in subdirectory" do_test 'mkdir dir; echo 888 > dir/file; echo 888 > dir/idfile2; ln -s ../dir dir/circle; ln -s file dir/filelink' msg "hard link group and identical files not hard linked" do_test 'echo 888 > file; echo 888 > file2; ln file link; ln file link2; echo 888 > file3' msg "C source code of wimlib" do_test 'cp $srcdir/src/*.c .' msg "tons of random stuff" do_test 'echo -n 8 > file; ln file hardlink; ln -s hardlink symlink; echo -n 8 > identical file; dd if=/dev/urandom of=randomfile bs=4096 count=10 &>/dev/null; mkdir dir; mkdir anotherdir; cp file anotherdir; ln file anotherdir/anotherhardlink; ln -s .. anotherdir/anothersymlink; ln -s anothersymlink anotherdir/symlinktosymlink; echo -n 33 > anotherfile; echo -n > emptyfile; mkdir dir/subdir; ln file dir/subdir/file; echo -n 8 > dir/subdir/file2; ln dir/subdir/file dir/subdir/link;' msg "timestamp before 1970 (before start of UNIX epoch)" do_test 'touch -t 196901231234.56 file' wimlib-1.13.1/tests/wlfuzz.c0000644000175000017500000006527013454544634012676 00000000000000/* * wlfuzz.c - Randomized tests for wimlib */ /* * Copyright (C) 2015-2016 Eric Biggers * * 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 Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * This program is a randomized test runner for wimlib. It must be linked * against a build of the library compiled with --enable-test-support. * * Various types of tests are run. Most important is the "apply and capture" * test, which works as follows: * * 1. Generate an in-memory WIM image containing a random directory tree * 2. Persist the image into a WIM file * 3. Apply the WIM image to somewhere * 4. Re-capture the applied image * 5. Compare the directory tree of the re-captured image to the original * * Note that this is an "apply and capture" test, not a "capture and apply" * test. By using the filesystem as the intermediary rather than as the * starting point and ending point, the tests will run nearly unchanged * regardless of filesystem type (e.g. UNIX, Windows, or NTFS-3G). This style * of test has been effective at finding bugs in wimlib as well as bugs in * NTFS-3G where its behavior differs from that of Windows. * * Care is taken to exercise different options, such as different compression * formats, when multiple are available. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifndef ENABLE_TEST_SUPPORT # error "This program requires that wimlib was configured with --enable-test-support." #endif #include #include #include #include #include #include #include #include #include #include #ifdef WITH_NTFS_3G # include #endif #include #ifdef __WIN32__ # include # include # include #endif #include "wimlib.h" #include "wimlib_tchar.h" #include "wimlib/test_support.h" #include "wimlib/wof.h" #ifndef O_BINARY # define O_BINARY 0 #endif #define ARRAY_LEN(A) (sizeof(A) / sizeof((A)[0])) #define TMP_TARGET_NAME T("wlfuzz-tmp-target") #define MAX_NUM_WIMS 4 static bool wimfile_in_use[MAX_NUM_WIMS]; static int in_use_wimfile_indices[MAX_NUM_WIMS]; static int num_wimfiles_in_use = 0; static void assertion_failed(int line, const char *format, ...) { va_list va; va_start(va, format); fprintf(stderr, "ASSERTION FAILED at line %d: ", line); vfprintf(stderr, format, va); fputc('\n', stderr); va_end(va); exit(1); } #define ASSERT(expr, msg, ...) \ ({ \ if (__builtin_expect(!(expr), 0)) \ assertion_failed(__LINE__, (msg), ##__VA_ARGS__); \ }) #define CHECK_RET(ret) \ ({ \ int r = (ret); \ ASSERT(!r, "%"TS, wimlib_get_error_string(r)); \ }) static void change_to_temporary_directory(void) { #ifdef __WIN32__ ASSERT(SetCurrentDirectory(L"E:\\"), "failed to change directory to E:\\"); #else const char *tmpdir = getenv("TMPDIR"); if (!tmpdir) tmpdir = P_tmpdir; ASSERT(!chdir(tmpdir), "failed to change to temporary directory \"%s\": %m", tmpdir); #endif } static void __attribute__((unused)) copy_file(const tchar *src, const tchar *dst) { int in_fd = topen(src, O_RDONLY|O_BINARY); int out_fd = topen(dst, O_WRONLY|O_TRUNC|O_CREAT|O_BINARY, 0644); char buf[32768]; ssize_t bytes_read, bytes_written, i; ASSERT(in_fd >= 0, "%"TS": open error: %m", src); ASSERT(out_fd >= 0, "%"TS": open error: %m", dst); while ((bytes_read = read(in_fd, buf, sizeof(buf))) > 0) { for (i = 0; i < bytes_read; i += bytes_written) { bytes_written = write(out_fd, &buf[i], bytes_read - i); ASSERT(bytes_written > 0, "%"TS": write error: %m", dst); } } ASSERT(bytes_read == 0, "%"TS": read error: %m", src); close(in_fd); close(out_fd); } #ifdef WITH_NTFS_3G static void create_ntfs_volume(const char *name) { int fd; int pid; int status; static const char buffer[1] = {0}; fd = open(name, O_WRONLY|O_TRUNC|O_CREAT|O_NOFOLLOW, 0644); ASSERT(fd >= 0, "%s: open error: %m", name); ASSERT(lseek(fd, 999999999, SEEK_SET) != -1, "%s: lseek error: %m", name); ASSERT(write(fd, buffer, 1) == 1, "%s: write error: %m", name); ASSERT(close(fd) == 0, "%s: close error: %m", name); pid = fork(); ASSERT(pid >= 0, "fork error: %m"); if (pid == 0) { close(STDOUT_FILENO); close(STDERR_FILENO); execlp("mkntfs", "mkntfs", "--force", "--fast", name, (char *)NULL); ASSERT(false, "Failed to execute mkntfs: %m"); } ASSERT(wait(&status) != -1, "wait error: %m"); ASSERT(WIFEXITED(status) && WEXITSTATUS(status) == 0, "mkntfs error: exited with status %d", status); } #endif /* WITH_NTFS_3G */ #ifdef __WIN32__ extern WINAPI NTSTATUS NtQueryDirectoryFile (HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass, BOOLEAN ReturnSingleEntry, PUNICODE_STRING FileName, BOOLEAN RestartScan); static void delete_directory_tree_recursive(HANDLE cur_dir, UNICODE_STRING *name) { OBJECT_ATTRIBUTES attr = { .Length = sizeof(attr), }; IO_STATUS_BLOCK iosb; FILE_BASIC_INFORMATION basic = { .FileAttributes = FILE_ATTRIBUTE_NORMAL, }; HANDLE h; const size_t bufsize = 8192; void *buf; NTSTATUS status; ULONG perms; ULONG flags; flags = FILE_DELETE_ON_CLOSE | FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT | FILE_SEQUENTIAL_ONLY; name->MaximumLength = name->Length; attr.RootDirectory = cur_dir; attr.ObjectName = name; perms = DELETE | SYNCHRONIZE | FILE_LIST_DIRECTORY | FILE_TRAVERSE; retry: status = NtOpenFile(&h, perms, &attr, &iosb, FILE_SHARE_VALID_FLAGS, flags); if (!NT_SUCCESS(status)) { if (status == STATUS_OBJECT_NAME_NOT_FOUND) return; if (status == STATUS_CANNOT_DELETE && (perms & DELETE)) { perms &= ~DELETE; flags &= ~FILE_DELETE_ON_CLOSE; perms |= FILE_WRITE_ATTRIBUTES; goto retry; } ASSERT(false, "NtOpenFile() for deletion failed; status=0x%08"PRIx32, status); } if (perms & FILE_WRITE_ATTRIBUTES) { status = NtSetInformationFile(h, &iosb, &basic, sizeof(basic), FileBasicInformation); NtClose(h); if (!NT_SUCCESS(status)) { ASSERT(false, "NtSetInformationFile() for deletion " "failed; status=0x%08"PRIx32, status); } perms &= ~FILE_WRITE_ATTRIBUTES; perms |= DELETE; flags |= FILE_DELETE_ON_CLOSE; goto retry; } buf = malloc(bufsize); ASSERT(buf != NULL, "out of memory!"); while (NT_SUCCESS(status = NtQueryDirectoryFile(h, NULL, NULL, NULL, &iosb, buf, bufsize, FileNamesInformation, FALSE, NULL, FALSE))) { const FILE_NAMES_INFORMATION *info = buf; for (;;) { if (!(info->FileNameLength == 2 && info->FileName[0] == L'.') && !(info->FileNameLength == 4 && info->FileName[0] == L'.' && info->FileName[1] == L'.')) { name->Buffer = (wchar_t *)info->FileName; name->Length = info->FileNameLength; delete_directory_tree_recursive(h, name); } if (info->NextEntryOffset == 0) break; info = (const FILE_NAMES_INFORMATION *) ((const char *)info + info->NextEntryOffset); } } ASSERT(status == STATUS_NO_MORE_FILES || /* end of directory */ status == STATUS_INVALID_PARAMETER, /* not a directory */ "NtQueryDirectoryFile() for deletion failed; " "status=0x%08"PRIx32, status); free(buf); NtClose(h); } static void delete_directory_tree(const wchar_t *name) { UNICODE_STRING uname; void *buffer; ASSERT(RtlDosPathNameToNtPathName_U(name, &uname, NULL, NULL), "Unable to translate %ls to NT namespace path", name); buffer = uname.Buffer; delete_directory_tree_recursive(NULL, &uname); HeapFree(GetProcessHeap(), 0, buffer); ASSERT(GetFileAttributes(name) == 0xFFFFFFFF, "Deletion didn't work!"); } #else /* __WIN32__ */ static void delete_directory_tree_recursive(int dirfd, const char *name) { int fd; DIR *dir; struct dirent *ent; if (!unlinkat(dirfd, name, 0) || errno == ENOENT) return; ASSERT(errno == EISDIR, "%s: unlink error: %m", name); fd = openat(dirfd, name, O_RDONLY | O_NOFOLLOW | O_DIRECTORY); ASSERT(fd >= 0, "%m"); dir = fdopendir(fd); ASSERT(dir != NULL, "%m"); while (errno = 0, (ent = readdir(dir))) if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, "..")) delete_directory_tree_recursive(fd, ent->d_name); closedir(dir); ASSERT(!unlinkat(dirfd, name, AT_REMOVEDIR), "%m"); } static void delete_directory_tree(const tchar *name) { delete_directory_tree_recursive(AT_FDCWD, name); } #endif /* !__WIN32__ */ static uint32_t rand32(void) { static uint64_t state; /* A simple linear congruential generator */ state = (state * 25214903917 + 11) & (((uint64_t)1 << 48) - 1); return state >> 16; } static inline bool randbool(void) { return rand32() & 1; } static tchar wimfile[32]; static const tchar * get_wimfile(int index) { tsprintf(wimfile, T("wim%d"), index); return wimfile; } static int select_random_wimfile_index(void) { return in_use_wimfile_indices[rand32() % num_wimfiles_in_use]; } static const tchar * select_new_wimfile(void) { int index = 0; while (wimfile_in_use[index]) index++; in_use_wimfile_indices[num_wimfiles_in_use++] = index; wimfile_in_use[index] = true; return get_wimfile(index); } static WIMStruct * open_wim(int index) { const tchar *wimfile = get_wimfile(index); WIMStruct *wim; int open_flags = 0; open_flags |= randbool() ? 0 : WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; printf("Opening %"TS" with flags 0x%08x\n", wimfile, open_flags); CHECK_RET(wimlib_open_wim(wimfile, open_flags, &wim)); return wim; } static WIMStruct * open_random_wim(void) { return open_wim(select_random_wimfile_index()); } static int get_image_count(WIMStruct *wim) { struct wimlib_wim_info info; CHECK_RET(wimlib_get_wim_info(wim, &info)); return info.image_count; } #ifdef __WIN32__ static bool is_wimboot_capable(WIMStruct *wim) { struct wimlib_wim_info info; CHECK_RET(wimlib_get_wim_info(wim, &info)); return info.wim_version == 0x10D00 && ((info.compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS && (info.chunk_size == 4096 || info.chunk_size == 8192 || info.chunk_size == 16384 || info.chunk_size == 32768)) || (info.compression_type == WIMLIB_COMPRESSION_TYPE_LZX && info.chunk_size == 32768)); } #endif /* __WIN32__ */ static void overwrite_wim(WIMStruct *wim) { int write_flags = 0; struct wimlib_wim_info info; CHECK_RET(wimlib_get_wim_info(wim, &info)); switch (rand32() % 4) { case 0: write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; break; case 1: write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY; break; } switch (rand32() % 8) { case 0: write_flags |= WIMLIB_WRITE_FLAG_PIPABLE; break; case 1: write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE; break; } write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_RECOMPRESS; write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_FSYNC; write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_REBUILD; write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_SOFT_DELETE; write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_IGNORE_READONLY_FLAG; write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_RETAIN_GUID; write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_SEND_DONE_WITH_FILE_MESSAGES; write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_NO_SOLID_SORT; if (rand32() % 8 == 0 && !(write_flags & WIMLIB_WRITE_FLAG_PIPABLE) && (!info.pipable || (write_flags & WIMLIB_WRITE_FLAG_NOT_PIPABLE))) write_flags |= WIMLIB_WRITE_FLAG_SOLID; if (randbool() && !info.pipable && !(write_flags & (WIMLIB_WRITE_FLAG_RECOMPRESS | WIMLIB_WRITE_FLAG_PIPABLE))) write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT; printf("overwrite with flags: 0x%08x\n", write_flags); CHECK_RET(wimlib_overwrite(wim, write_flags, 0)); } static int get_random_write_flags(void) { int write_flags = 0; write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_SEND_DONE_WITH_FILE_MESSAGES; write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_NO_SOLID_SORT; switch (rand32() % 8) { case 0: write_flags |= WIMLIB_WRITE_FLAG_PIPABLE; break; case 1: write_flags |= WIMLIB_WRITE_FLAG_SOLID; break; } return write_flags; } static uint32_t get_random_chunk_size(int min_order, int max_order) { return 1 << (min_order + (rand32() % (max_order - min_order + 1))); } static void op__create_new_wim(void) { printf(":::op__create_new_wim\n"); const tchar *wimfile; enum wimlib_compression_type ctype = WIMLIB_COMPRESSION_TYPE_NONE; uint32_t chunk_size = 0; uint32_t solid_chunk_size = 0; int write_flags; WIMStruct *wim; if (num_wimfiles_in_use == MAX_NUM_WIMS) return; wimfile = select_new_wimfile(); /* Select a random compression type and chunk size. */ switch (rand32() % 4) { case 0: break; case 1: ctype = WIMLIB_COMPRESSION_TYPE_XPRESS; chunk_size = get_random_chunk_size(12, 16); break; case 2: ctype = WIMLIB_COMPRESSION_TYPE_LZX; if (randbool()) chunk_size = 1 << 15; else chunk_size = get_random_chunk_size(15, 21); break; case 3: ctype = WIMLIB_COMPRESSION_TYPE_LZMS; chunk_size = get_random_chunk_size(15, 28); if (randbool()) solid_chunk_size = get_random_chunk_size(15, 26); else solid_chunk_size = get_random_chunk_size(26, 28); break; } /* Select random write flags. */ write_flags = get_random_write_flags(); printf("Creating %"TS" with write flags 0x%08x, compression_type=%"TS", chunk_size=%u, solid_chunk_size=%u\n", wimfile, write_flags, wimlib_get_compression_type_string(ctype), chunk_size, solid_chunk_size); CHECK_RET(wimlib_create_new_wim(ctype, &wim)); if (chunk_size != 0) CHECK_RET(wimlib_set_output_chunk_size(wim, chunk_size)); if (solid_chunk_size != 0) CHECK_RET(wimlib_set_output_pack_chunk_size(wim, solid_chunk_size)); CHECK_RET(wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES, write_flags, 0)); wimlib_free(wim); } static void op__add_empty_image_to_random_wim(void) { printf(":::op__add_empty_image_to_random_wim\n"); WIMStruct *wim; int new_idx; if (num_wimfiles_in_use < 1) return; wim = open_random_wim(); CHECK_RET(wimlib_add_empty_image(wim, NULL, &new_idx)); printf("Adding empty image to %"TS" at index %d\n", wimfile, new_idx); overwrite_wim(wim); wimlib_free(wim); } static void op__delete_random_image_from_random_wim(void) { printf(":::op__delete_random_image_from_random_wim\n"); WIMStruct *wim; int image; int image_count; if (num_wimfiles_in_use == 0) return; wim = open_random_wim(); image_count = get_image_count(wim); if (image_count != 0) { image = 1 + (rand32() % image_count); CHECK_RET(wimlib_delete_image(wim, image)); printf("Deleting image %d from %"TS"\n", image, wimfile); overwrite_wim(wim); } wimlib_free(wim); } static void op__delete_random_wim(void) { printf(":::op__delete_random_wim\n"); const tchar *wimfile; int which; int index; if (num_wimfiles_in_use == 0) return; which = rand32() % num_wimfiles_in_use; index = in_use_wimfile_indices[which]; wimfile = get_wimfile(index); ASSERT(!tunlink(wimfile), "failed to unlink %"TS": %m", wimfile); printf("Deleted %"TS"\n", wimfile); for (int i = which; i < num_wimfiles_in_use - 1; i++) in_use_wimfile_indices[i] = in_use_wimfile_indices[i + 1]; num_wimfiles_in_use--; wimfile_in_use[index] = false; } static void op__verify_random_wim(void) { printf(":::op__verify_random_wim\n"); WIMStruct *wim; if (num_wimfiles_in_use == 0) return; wim = open_random_wim(); CHECK_RET(wimlib_verify_wim(wim, 0)); printf("Verified %"TS"\n", wimfile); wimlib_free(wim); } static void op__overwrite_with_no_changes(void) { printf(":::op__overwrite_with_no_changes\n"); WIMStruct *wim; if (num_wimfiles_in_use == 0) return; wim = open_random_wim(); overwrite_wim(wim); wimlib_free(wim); } static void op__export_random_image(void) { printf(":::op__export_random_image\n"); int src_wimfile_index; int dst_wimfile_index; WIMStruct *src_wim; WIMStruct *dst_wim; int src_image_count; int dst_image_count; int src_image; int dst_image; if (num_wimfiles_in_use < 2) return; src_wimfile_index = select_random_wimfile_index(); do { dst_wimfile_index = select_random_wimfile_index(); } while (dst_wimfile_index == src_wimfile_index); src_wim = open_wim(src_wimfile_index); dst_wim = open_wim(dst_wimfile_index); src_image_count = get_image_count(src_wim); dst_image_count = get_image_count(dst_wim); /* Choose a random source image --- single or all. */ src_image = WIMLIB_ALL_IMAGES; if (src_image_count != 0 && randbool()) src_image = 1 + (rand32() % src_image_count); printf("Exporting image %d of %d from wim %d into wim %d\n", src_image, src_image_count, src_wimfile_index, dst_wimfile_index); CHECK_RET(wimlib_export_image(src_wim, src_image, dst_wim, NULL, NULL, 0)); overwrite_wim(dst_wim); wimlib_free(dst_wim); dst_wim = open_wim(dst_wimfile_index); /* Compare the images. */ dst_image = dst_image_count; for (int image = (src_image == WIMLIB_ALL_IMAGES ? 1 : src_image); image <= (src_image == WIMLIB_ALL_IMAGES ? src_image_count : src_image); image++) { CHECK_RET(wimlib_compare_images(src_wim, image, dst_wim, ++dst_image, 0)); } wimlib_free(src_wim); wimlib_free(dst_wim); } static void op__apply_and_capture_test(void) { printf(":::op__apply_and_capture_test\n"); WIMStruct *wim; int image; int index; int extract_flags = 0; int add_flags = 0; int cmp_flags = 0; if (num_wimfiles_in_use == 0) return; /* Generate a random image. */ index = select_random_wimfile_index(); wim = open_wim(index); CHECK_RET(wimlib_add_image(wim, (void *)rand32, NULL, NULL, WIMLIB_ADD_FLAG_GENERATE_TEST_DATA | WIMLIB_ADD_FLAG_NORPFIX)); image = get_image_count(wim); printf("generated wim%d image %d\n", index, image); { /* * Compare the in-memory version of the generated image with a * version written to disk */ WIMStruct *tmp_wim; CHECK_RET(wimlib_write(wim, T("tmp.wim"), image, 0, 0)); CHECK_RET(wimlib_open_wim(T("tmp.wim"), 0, &tmp_wim)); CHECK_RET(wimlib_compare_images(wim, image, tmp_wim, 1, 0)); wimlib_free(tmp_wim); } overwrite_wim(wim); wimlib_free(wim); /* Apply the generated image. */ wim = open_wim(index); delete_directory_tree(TMP_TARGET_NAME); #ifdef WITH_NTFS_3G if (rand32() & 1) { printf("applying in NTFS mode\n"); extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS; extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS; extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES; extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_TIMESTAMPS; add_flags |= WIMLIB_ADD_FLAG_NTFS; cmp_flags |= WIMLIB_CMP_FLAG_NTFS_3G_MODE; create_ntfs_volume(TMP_TARGET_NAME); } else #endif { #ifdef __WIN32__ printf("applying in Windows mode\n"); cmp_flags |= WIMLIB_CMP_FLAG_WINDOWS_MODE; #else /* __WIN32__ */ printf("applying in UNIX mode\n"); extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA; add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA; cmp_flags |= WIMLIB_CMP_FLAG_UNIX_MODE; #endif /* !__WIN32__ */ } add_flags |= WIMLIB_ADD_FLAG_NORPFIX; CHECK_RET(wimlib_extract_image(wim, image, TMP_TARGET_NAME, extract_flags)); /* Sometimes extract twice so that we test overwriting existing files. */ if (!(extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) && randbool()) { CHECK_RET(wimlib_extract_image(wim, image, TMP_TARGET_NAME, extract_flags)); } /* Capture the applied image. */ CHECK_RET(wimlib_add_image(wim, TMP_TARGET_NAME, NULL, NULL, add_flags)); overwrite_wim(wim); wimlib_free(wim); /* Compare the generated image with the captured image. */ wim = open_wim(index); CHECK_RET(wimlib_compare_images(wim, image, wim, image + 1, cmp_flags)); wimlib_free(wim); } #ifdef __WIN32__ /* Enumerate and unregister all backing WIMs from the specified volume */ static void unregister_all_backing_wims(const tchar drive_letter) { wchar_t volume[7]; HANDLE h; void *overlay_list; DWORD bytes_returned; const struct wim_provider_overlay_entry *entry; struct { struct wof_external_info wof_info; struct wim_provider_remove_overlay_input wim; } in; wsprintf(volume, L"\\\\.\\%lc:", drive_letter); h = CreateFile(volume, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_VALID_FLAGS, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); ASSERT(h != INVALID_HANDLE_VALUE, "Failed to open %ls; error=%u", volume, (unsigned)GetLastError()); overlay_list = malloc(32768); ASSERT(overlay_list != NULL, "out of memory"); in.wof_info.version = WOF_CURRENT_VERSION; in.wof_info.provider = WOF_PROVIDER_WIM; if (!DeviceIoControl(h, FSCTL_ENUM_OVERLAY, &in, sizeof(struct wof_external_info), overlay_list, 32768, &bytes_returned, NULL)) { ASSERT(GetLastError() == ERROR_INVALID_FUNCTION || GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == ERROR_FILE_NOT_FOUND, "FSCTL_ENUM_OVERLAY failed; error=%u", GetLastError()); return; } entry = overlay_list; for (;;) { printf("Unregistering data source ID %"PRIu64"\n", entry->data_source_id); in.wim.data_source_id = entry->data_source_id; ASSERT(DeviceIoControl(h, FSCTL_REMOVE_OVERLAY, &in, sizeof(in), NULL, 0, &bytes_returned, NULL), "FSCTL_REMOVE_OVERLAY failed; error=%u", (unsigned )GetLastError()); if (entry->next_entry_offset == 0) break; entry = (const struct wim_provider_overlay_entry *) ((const uint8_t *)entry + entry->next_entry_offset); } free(overlay_list); CloseHandle(h); } static void op__wimboot_test(void) { int index; int index2; WIMStruct *wim; WIMStruct *wim2; int image_count; int image; if (num_wimfiles_in_use == 0) return; index = select_random_wimfile_index(); unregister_all_backing_wims(L'E'); copy_file(get_wimfile(index), L"wimboot.wim"); CHECK_RET(wimlib_open_wim(L"wimboot.wim", 0, &wim)); image_count = get_image_count(wim); if (image_count == 0 || !is_wimboot_capable(wim)) { wimlib_free(wim); return; } image = 1 + (rand32() % image_count); printf("WIMBOOT test; wim%d image %d\n", index, image); delete_directory_tree(TMP_TARGET_NAME); CHECK_RET(wimlib_extract_image(wim, image, TMP_TARGET_NAME, WIMLIB_EXTRACT_FLAG_WIMBOOT)); if (randbool()) { CHECK_RET(wimlib_extract_image(wim, image, TMP_TARGET_NAME, WIMLIB_EXTRACT_FLAG_WIMBOOT)); } index2 = select_random_wimfile_index(); wim2 = open_wim(index2); image_count = get_image_count(wim2); CHECK_RET(wimlib_add_image(wim2, TMP_TARGET_NAME, NULL, NULL, WIMLIB_ADD_FLAG_NORPFIX)); overwrite_wim(wim2); wimlib_free(wim2); wim2 = open_wim(index2); printf("comparing wimboot.wim:%d with wim%d:%d\n", image, index2, image_count + 1); CHECK_RET(wimlib_compare_images(wim, image, wim2, image_count + 1, WIMLIB_CMP_FLAG_WINDOWS_MODE)); wimlib_free(wim); wimlib_free(wim2); } #endif /* __WIN32__ */ static int is_solid_resource(const struct wimlib_resource_entry *resource, void *_ctx) { return resource->packed; } static bool wim_contains_solid_resources(WIMStruct *wim) { return wimlib_iterate_lookup_table(wim, 0, is_solid_resource, NULL); } static void op__split_test(void) { printf(":::op__split_test\n"); WIMStruct *wim; WIMStruct *swm; WIMStruct *joined_wim; uint64_t part_size; int write_flags; const tchar *globs[] = { T("tmp*.swm") }; int image_count; if (num_wimfiles_in_use == 0) return; /* split, join, and compare */ wim = open_random_wim(); if (wim_contains_solid_resources(wim)) { /* Can't split a WIM containing solid resources */ wimlib_free(wim); return; } image_count = get_image_count(wim); part_size = 10000 + (rand32() % 1000000); write_flags = get_random_write_flags(); write_flags &= ~WIMLIB_WRITE_FLAG_SOLID; printf("splitting WIM %"TS": part_size=%"PRIu64", write_flags=0x%08x\n", wimfile, part_size, write_flags); CHECK_RET(wimlib_split(wim, T("tmp.swm"), part_size, write_flags)); CHECK_RET(wimlib_open_wim(T("tmp.swm"), WIMLIB_OPEN_FLAG_CHECK_INTEGRITY, &swm)); CHECK_RET(wimlib_reference_resource_files(swm, globs, 1, WIMLIB_REF_FLAG_GLOB_ENABLE | WIMLIB_REF_FLAG_GLOB_ERR_ON_NOMATCH, WIMLIB_OPEN_FLAG_CHECK_INTEGRITY)); CHECK_RET(wimlib_verify_wim(swm, 0)); CHECK_RET(wimlib_write(swm, T("joined.wim"), WIMLIB_ALL_IMAGES, write_flags, 0)); wimlib_free(swm); CHECK_RET(wimlib_open_wim(T("joined.wim"), 0, &joined_wim)); for (int i = 1; i <= image_count; i++) CHECK_RET(wimlib_compare_images(wim, 1, joined_wim, 1, 0)); CHECK_RET(wimlib_verify_wim(joined_wim, 0)); wimlib_free(joined_wim); wimlib_free(wim); tunlink(T("tmp.swm")); for (int i = 2; ; i++) { tchar name[32]; tsprintf(name, T("tmp%d.swm"), i); if (tunlink(name)) break; } } static void op__set_compression_level(void) { printf(":::op__set_compression_level\n"); unsigned int level = rand32() % 100; printf("Changing compression levels to %d\n", level); wimlib_set_default_compression_level(-1, level); } typedef void (*operation_func)(void); static const operation_func operation_table[] = { op__create_new_wim, op__add_empty_image_to_random_wim, op__delete_random_image_from_random_wim, op__delete_random_wim, op__delete_random_wim, op__verify_random_wim, op__overwrite_with_no_changes, op__export_random_image, op__apply_and_capture_test, op__apply_and_capture_test, op__apply_and_capture_test, op__apply_and_capture_test, op__apply_and_capture_test, op__split_test, op__set_compression_level, #ifdef __WIN32__ op__wimboot_test, #endif }; #ifdef __WIN32__ extern int wmain(int argc, wchar_t **argv); #define main wmain #endif int main(int argc, tchar **argv) { unsigned long long num_iterations; if (argc < 2) { num_iterations = ULLONG_MAX; printf("Starting test runner\n"); } else { num_iterations = tstrtoull(argv[1], NULL, 10); printf("Starting test runner with %llu iterations\n", num_iterations); } CHECK_RET(wimlib_global_init(0)); wimlib_set_print_errors(true); change_to_temporary_directory(); for (int i = 0; i < MAX_NUM_WIMS; i++) ASSERT(!tunlink(get_wimfile(i)) || errno == ENOENT, "unlink: %m"); for (unsigned long long i = 0; i < num_iterations; i++) { printf("--> iteration %llu\n", i); (*operation_table[rand32() % ARRAY_LEN(operation_table)])(); } wimlib_global_cleanup(); return 0; } wimlib-1.13.1/tests/test-imagex-ntfs0000744000175000017500000001765713160354225014307 00000000000000#!/usr/bin/env bash # This script does some sanity testing of the 'imagex' program, specifically # checking the NTFS capture and apply features. # # This test will fail if wimlib was compiled with --without-ntfs-3g. # # Please note that cleanup is not done if a test fails, and NTFS volumes may # remain mounted. set -e cd tests srcdir="${srcdir:-.}/.." srcdir="$(cd $srcdir; pwd)" . "$srcdir/tests/test_utils.sh" TEST_SUBDIR=tmpdir_test-imagex-ntfs __do_unmount() { for ((i = 0; i < 10; i++)); do if fusermount -z -u $1; then return 0 else sleep 1 fi done error "Failed to unmount \"$1\"" } do_unmount() { if mountpoint $1 &> /dev/null; then __do_unmount $1 fi } skip_test() { cd .. cleanup exit 77 } __do_mount() { if ! ntfs-3g -o "no_def_opts,silent" $1 $2; then if [ $UID -ne 0 ] && [ "$3" = "nofail" ]; then echo "WARNING: skipping NTFS tests because we aren't able to " echo "mount an NTFS volume (perhaps ntfs-3g is not installed setuid root?)" skip_test else error "Could not mount NTFS volume \"$1\" on \"$2\"! Make sure ntfs-3g is "\ "installed, and that you are either running the tests as root or have ntfs-3g "\ "installed setuid root, so that we can mount an NTFS volume." fi fi } do_mount() { do_unmount $2 __do_mount $1 $2 $3 } do_mkntfs() { if ! mkntfs --force --fast $1 > /dev/null; then error "Could not create NTFS volume \"$1\"! Make sure ntfsprogs are installed." fi } init() { echo "Creating NTFS volumes and empty directories to use as mountpoints" dd if=/dev/zero of=in.ntfs bs=4096 count=1000 &> /dev/null dd if=/dev/zero of=out.ntfs bs=4096 count=1000 &> /dev/null mkdir in.mnt out.mnt do_mkntfs in.ntfs do_mkntfs out.ntfs do_mount in.ntfs in.mnt nofail } cleanup() { do_unmount $TEST_SUBDIR/in.mnt do_unmount $TEST_SUBDIR/out.mnt rm -rf $TEST_SUBDIR } do_test() { cd in.mnt eval "$1" cd .. __do_unmount in.mnt if ! wimcapture in.ntfs ntfs.wim; then error "Failed to capture NTFS volume into a WIM" fi if ! wimapply ntfs.wim 1 out.ntfs; then error "Failed to apply WIM to NTFS volume" fi __do_mount in.ntfs in.mnt noatime __do_mount out.ntfs out.mnt noatime if [ -x /usr/bin/tree ]; then tree in.mnt --inodes -F -s --noreport fi if ! ../tree-cmp in.mnt out.mnt NTFS; then if [ -x /usr/bin/tree ]; then echo "Dumping tree of applied image" tree out.mnt --inodes -F -s --noreport error 'Information was lost or corrupted while capturing and then applying an NTFS volume' fi fi rm -rf out.mnt/* in.mnt/* __do_unmount out.mnt } msg() { echo "--------------------------------------------------------------------" echo "Testing image capture and application of NTFS volume containing $1" echo "--------------------------------------------------------------------" } cleanup mkdir $TEST_SUBDIR cd $TEST_SUBDIR init msg "Empty NTFS volume" do_test "" msg "a single file" do_test "echo 1 > file" msg "a single directory" do_test "mkdir dir" msg "subdirectory with file" do_test "mkdir dir; echo 1 > dir/file" msg "empty file" do_test "echo -n > empty_file" msg "two empty files" do_test "echo -n > empty_file_1; echo -n > empty_file_2" msg "hard link in same directory" do_test "echo 1 > file; ln file link" msg "hard link between empty files" do_test "echo -n > empty_file; ln empty_file link" msg "relative symbolic link" do_test "echo 1 > file; ln -s file symlink" msg "absolute symbolic link" do_test "echo 1 > file; ln -s /some/absolute/target symlink" msg "large file" do_test "dd if=/dev/zero of=file bs=4096 count=10 &> /dev/null" msg "file with DOS name" do_test "echo 1 > file; setfattr -v file -n system.ntfs_dos_name file" msg "many nested directories" do_test 'mkdir dir; mkdir dir/subdir; mkdir dir/subdir/subdir2; mkdir dir/subdir/subdir3' msg "identical files and symlinks in subdirectory" do_test 'mkdir dir; echo 888 > dir/file; echo 888 > dir/idfile2; ln -s ../dir dir/circle; ln -s file dir/filelink' msg "hard link group and identical files not hard linked" do_test 'echo 888 > file; echo 888 > file2; ln file link; ln file link2; echo 888 > file3' msg "file with named data stream" do_test 'echo 1 > file; setfattr -n user.ads -v 2 file' msg "file with multiple named data streams" do_test 'echo 1 > file; setfattr -n user.a -v 1 file; setfattr -n user.aa -v 11 file; setfattr -n user.aaa -v 111 file; setfattr -n user.aaaa -v 1111 file' msg "file with multiple named data streams with same contents" do_test 'echo 1 > file; setfattr -n user.a -v 1111 file; setfattr -n user.aa -v 1111 file; setfattr -n user.aaa -v 1111 file; setfattr -n user.aaaa -v 1111 file;' msg "file with named data streams with same contents as other file" do_test 'echo -n > file; setfattr -n user.a -v 1111 file; echo -n 1111 > otherfile;' msg "file with empty named data stream and non-empty unnamed data stream" do_test 'echo 1 > file; setfattr -n user.ads -v 0x file;' msg "file with empty named data stream and empty unnamed data stream" do_test 'echo -n > file; setfattr -n user.ads -v 0x file;' msg "file with named data stream with hardlink" do_test 'echo 999 > file; setfattr -n user.ads -v 0x123456 file; ln file link;' msg "C source code of wimlib" do_test 'cp $srcdir/src/*.c .' msg "file with security descriptor" do_test 'touch file; setfattr -n system.ntfs_acl -v 0s`cat $srcdir/tests/security_descriptor_1.base64` file' msg "file with object ID" do_test 'touch file; touch file2; setfattr -n system.ntfs_object_id -v 0x15ac83a36dc6cf8ec459b8017dd8626f file setfattr -n system.ntfs_object_id -v 0xf67394c12b17608e1d050d181ba8ffd27df80cbdf620f4c82c79b9e6799147b697621aff72915ade05abb96b15dea1a3e0bda4caa9e33cfd461c92c16be9713d file2' msg "files with different security descriptors" do_test 'touch file; touch file2; setfattr -n system.ntfs_acl -v 0s`cat $srcdir/tests/security_descriptor_1.base64` file setfattr -n system.ntfs_acl -v 0s`cat $srcdir/tests/security_descriptor_2.base64` file' msg "files with different security descriptors and some with the same security descriptor" do_test 'touch file; touch file2; touch file3; mkdir dir; setfattr -n system.ntfs_acl -v 0s`cat $srcdir/tests/security_descriptor_1.base64` file setfattr -n system.ntfs_acl -v 0s`cat $srcdir/tests/security_descriptor_2.base64` file setfattr -n system.ntfs_acl -v 0s`cat $srcdir/tests/security_descriptor_1.base64` dir setfattr -n system.ntfs_acl -v 0s`cat $srcdir/tests/security_descriptor_1.base64` file3' msg "tons of random stuff" do_test 'echo -n 8 > file; ln file hardlink; ln -s hardlink symlink; echo -n 8 > identical file; dd if=/dev/urandom of=randomfile bs=4096 count=10 &>/dev/null; mkdir dir; setfattr -n system.ntfs_dos_name -v DOSNAME dir; setfattr -n system.ntfs_acl -v 0s`cat $srcdir/tests/security_descriptor_1.base64` dir mkdir anotherdir; cp file anotherdir; ln file anotherdir/anotherhardlink; ln -s .. anotherdir/anothersymlink; ln -s anothersymlink anotherdir/symlinktosymlink; echo -n 33 > anotherfile; setfattr -n user.ads anotherfile -v 33; setfattr -n user.ads2 anotherfile -v 8; setfattr -n user.ads3 anotherfile -v 33; echo -n > emptyfile; setfattr -n user.ads emptyfile -v 8; setfattr -n user.ads5 emptyfile -v"`cat $srcdir/src/sha1.c`" mkdir dir/subdir; ln file dir/subdir/file; echo -n 8 > dir/subdir/file2; ln dir/subdir/file dir/subdir/link; echo -n > dir/subdir/empty; setfattr -n system.ntfs_dos_name -v 123 dir/subdir/empty; setfattr -n system.ntfs_acl -v 0s`cat $srcdir/tests/security_descriptor_1.base64` dir/subdir/link; setfattr -n user.yet_another_ads -v "" dir/subdir/link; setfattr -n user.yet_another_ads2 -v "" dir/subdir/link; setfattr -n user.yet_another_ads3 -v "abc" dir/subdir/link; setfattr -n user.yet_another_ads4 -v "" dir/subdir/link;' echo "**********************************************************" echo " NTFS capture/apply tests passed " echo "**********************************************************" cd .. cleanup wimlib-1.13.1/tests/test-imagex0000744000175000017500000003675213324707716013345 00000000000000#!/usr/bin/env bash # This script does some sanity testing of the 'wimlib-imagex' program. It by no # means tests every aspect of wimlib comprehensively. set -e cd tests srcdir="${srcdir:-.}/.." srcdir="$(cd $srcdir; pwd)" . "$srcdir/tests/test_utils.sh" TEST_SUBDIR=tmpdir_test-imagex # Execute the tests in a subdirectory to avoid conflicts with concurrent tests default_cleanup mkdir $TEST_SUBDIR cd $TEST_SUBDIR # Make test directory mkdir dir cp $srcdir/src/*.c dir mkdir dir/subdir echo 'hello' > dir/subdir/hello echo 'hello' > dir/subdir/hello2 ln dir/subdir/hello dir/subdir/hellolink echo -n > dir/subdir/empty_file ln -s hello dir/subdir/rel_symlink mkdir dir2 echo 'testing' > dir2/file dd if=/dev/zero of=dir2/zeroes bs=4096 count=5 # Capturing and applying WIM with None, LZX, and XPRESS compression for comp_type in None LZX XPRESS; do echo "Testing capture and application of $comp_type-compressed WIM" if ! wimcapture dir dir.wim --compress=$comp_type; then error "'wimcapture' failed" fi if ! wimapply dir.wim tmp; then error "'wimapply' failed" fi if ! test `wim_ctype dir.wim` = "$comp_type"; then error "'wiminfo' didn't report the compression type correctly" fi if ! diff -q -r dir tmp; then error "Recursive diff of extracted directory with original failed" fi if ! test `get_link_count tmp/subdir/hello` = 2; then error "Incorrect number of hard links in extracted file" fi if ! test `get_inode_number tmp/subdir/hello` != `get_inode_number tmp/subdir/hello2`; then error "Expected different inode numbers in files not hard-linked" fi if ! test "`get_inode_number tmp/subdir/hello`" = "`get_inode_number tmp/subdir/hellolink`"; then error "Expected same inode numbers in hard-linked files" fi if ! test -L tmp/subdir/rel_symlink; then error "Symlink not extracted correctly" fi if ! test "`readlink tmp/subdir/rel_symlink`" = "hello"; then error "Symlink target not correct" fi rm -rf dir.wim tmp done # Test wimappend --create rm -f dir.wim if wimappend dir dir.wim; then error "wimappend to nonexisting file unexpectedly succeeded" fi if ! wimappend dir dir.wim --create; then error "wimappend --create to nonexisting file failed" fi if ! wimappend dir dir.wim --create; then error "wimappend --create to existing file failed" fi if ! test "`wiminfo dir.wim | grep 'Image Count' | awk '{print $3}'`" = 2; then error "Incorrect WIM image count after wimappend --create" fi # Capturing and modifying name, description, and bootable flag echo "Testing capture of WIM with default name and description" wimcapture dir dir.wim if ! test "`wiminfo dir.wim | grep Name | awk '{print $2}'`" = "dir"; then error "WIM name not set correctly" fi if ! test "`wiminfo dir.wim | grep Description | awk '{print $2}'`" = ""; then error "WIM description not set correctly" fi echo "Testing capture of WIM with default boot flag" wimcapture dir dir.wim if ! test "`wiminfo dir.wim | grep '^Boot Index' | awk '{print $3}'`" = "0"; then error "WIM boot flag not set correctly" fi echo "Testing changing image bootable flag" if ! wiminfo dir.wim 1 --boot; then error "Failed to change bootable image" fi if ! test "`wiminfo dir.wim | grep '^Boot Index' | awk '{print $3}'`" = "1"; then error "Bootable image not changed correctly" fi echo "Testing changing image bootable flag" if ! wiminfo dir.wim 0 --boot; then error "Failed to reset bootable image" fi if ! test "`wiminfo dir.wim | grep '^Boot Index' | awk '{print $3}'`" = "0"; then error "Bootable image not reset correctly" fi echo "Testing changing image bootable flag to invalid image (this should generate errors)" if wiminfo dir.wim 2 --boot; then error "Succeeded in changing bootable image to invalid number" fi if ! test "`wiminfo dir.wim | grep '^Boot Index' | awk '{print $3}'`" = "0"; then error "Boot flag was changed even though the change command was supposed to fail" fi rm -rf dir.wim tmp echo "Testing capture of WIM with name and description" if ! wimcapture dir dir.wim "myname" "mydesc"; then error "Failed to capture WIM with specified name and description" fi if ! test "`wiminfo dir.wim | grep Name | awk '{print $2}'`" = "myname"; then error "WIM name not set correctly" fi if ! test "`wiminfo dir.wim | grep Description | awk '{print $2}'`" = "mydesc"; then error "WIM name not set correctly" fi echo "Testing printing WIM lookup table" if ! wiminfo --lookup-table dir.wim > /dev/null; then error "Failed to print WIM lookup table" fi echo "Testing printing WIM header" if ! wiminfo --header dir.wim > /dev/null; then error "Failed to print WIM header" fi echo "Testing printing WIM XML info" if ! wiminfo --xml dir.wim > /dev/null; then error "Failed to print WIM XML data" fi echo "Testing extracting WIM XML info" if ! wiminfo --extract-xml=dir.xml dir.wim; then error "Failed to extract WIM XML data" fi echo "Testing printing WIM metadata" if ! wimdir --detailed dir.wim > /dev/null; then error "Failed to print WIM metadata" fi rm -rf dir.wim tmp dir.xml echo "Testing capture of bootable WIM" if ! wimcapture dir dir.wim --boot; then error "Failed to capture bootable WIM" fi if ! test "`wiminfo dir.wim | grep '^Boot Index' | awk '{print $3}'`" = "1"; then error "Boot flag on bootable WIM not set correctly" fi rm -rf dir.wim tmp # Integrity table echo "Testing capture of WIM with integrity table" if ! wimcapture dir dir.wim --check; then error "Failed to capture WIM with integrity table" fi if ! wiminfo dir.wim | grep -q Integrity; then error "Integrity table on WIM not made" fi if ! wimapply --check dir.wim tmp; then error "Integrity table on WIM not made correctly" fi if ! diff -q -r dir tmp; then error "Recursive diff of applied WIM with original directory failed" fi rm -rf dir.wim tmp # Appending and deleting images echo "Testing appending WIM image" wimcapture dir dir.wim if ! wimappend dir2 dir.wim; then error "Appending WIM image failed" fi if ! test "`wiminfo dir.wim | grep 'Image Count' | awk '{print $3}'`" = 2; then error "WIM image count not correct" fi echo "Testing appending WIM image with existing name (this should generate errors)" if wimappend dir2 dir.wim "dir"; then error "Adding duplicate image name didn't fail" fi echo "Testing appending WIM image with new name" if ! wimappend dir2 dir.wim "newname"; then error "Appending WIM image failed" fi echo "Testing appending WIM image with integrity check" if ! wimappend dir2 dir.wim "newname2" --check; then error "Appending WIM image failed" fi if ! wiminfo dir.wim | grep -q Integrity; then error "Integrity table not set correctly on image append" fi echo "Testing appending WIM image with no integrity check" if ! wimappend dir2 dir.wim "newname3" --nocheck; then error "Appending WIM image failed" fi if wiminfo dir.wim | grep -q Integrity; then error "WIM integrity table not removed" fi # 5 images at this point if ! test "`wiminfo dir.wim | grep 'Image Count' | awk '{print $3}'`" = 5; then error "WIM does not contain the expected 5 images" fi echo "Testing deleting first WIM image" if ! wimdelete dir.wim 1; then error "Failed to delete WIM image" fi if ! test "`wiminfo dir.wim | grep 'Image Count' | awk '{print $3}'`" = 4; then error "WIM image not deleted correctly" fi echo "Testing deleting last WIM image" if ! wimdelete dir.wim 4; then error "Failed to delete WIM image" fi if ! test "`wiminfo dir.wim | grep 'Image Count' | awk '{print $3}'`" = 3; then error "WIM image not deleted correctly" fi echo "Testing deleting invalid WIM image (this should generate errors)" if wimdelete dir.wim 4; then error "Expected to fail to delete non-existent WIM image" fi if ! test "`wiminfo dir.wim | grep 'Image Count' | awk '{print $3}'`" = 3; then error "Image count changed even though we intentionally failed to delete an image" fi echo "Testing deleting all WIM images" if ! wimdelete dir.wim all; then error "Failed to delete all images from WIM" fi if ! test "`wiminfo dir.wim | grep 'Image Count' | awk '{print $3}'`" = 0; then error "Couldn't delete all WIM images correctly" fi echo "Testing appending directory to empty WIM and making it bootable" if ! wimappend dir dir.wim "myname" "mydesc" --check --boot; then error "Couldn't append named, described, bootable image to empty WIM with integrity check" fi if ! wiminfo dir.wim | grep -q Integrity; then error "Integrity check not found" fi if ! test "`wiminfo dir.wim | grep '^Boot Index' | awk '{print $3}'`" = "1"; then error "Bootable image not set correctly" fi echo "Testing appending non-directory (should generate errors)" if wimappend dir.wim dir.wim; then error "Incorrectly succeeded to append non-directory to WIM" fi echo "Testing appending non-existent file (should generate errors)" if wimappend SOME_NONEXISTENT_FILE dir.wim; then error "Incorrectly succeeded to append non-existent file to WIM" fi if [ `id -u` != 0 ]; then echo "Testing appending directory containing unreadable file (should generate errors)" mkdir -p dir3 echo 1 > dir3/file chmod -r dir3/file if wimappend dir3 dir.wim; then error "Incorrectly succeeded in capturing directory with unreadable file" fi fi rm -rf dir3 dir.wim # Applying multiple images, applying with hardlinks/symlinks echo "Testing application of multiple images" if ! wimcapture dir dir.wim; then error "Failed to prepare test WIM" fi if ! wimappend dir dir.wim "myname"; then error "Failed to append image to test WIM" fi if ! wimapply dir.wim all tmp; then error "Applying multiple images failed" fi if ! diff -q -r tmp/dir tmp/myname || ! diff -q -r dir tmp/dir; then error "Recursive diff of applied WIM with original directory failed" fi if test "`get_link_count tmp/dir/write.c`" != 1; then error "Incorrect link count on extracted file" fi if test "`get_link_count tmp/myname/write.c`" != 1; then error "Incorrect link count on extracted file" fi if test "`get_inode_number tmp/myname/write.c`" = "`get_inode_number tmp/dir/write.c`"; then error "Incorrect inode number" fi rm -rf tmp echo "Testing application of single image containing identical files" if ! wimapply dir.wim 1 tmp; then error "Failed to apply WIM" fi if test "`get_link_count tmp/subdir/hello`" != 2; then error "Incorrect link count on extracted file" fi if test "`get_link_count tmp/subdir/hello2`" != 1; then error "Incorrect link count on extracted file" fi if test "`get_inode_number tmp/subdir/hello`" = "`get_inode_number tmp/subdir/hello2`"; then error "Inode numbers on non-hard-linked files are the same" fi if test "`get_inode_number tmp/subdir/hello`" != "`get_inode_number tmp/subdir/hellolink`"; then error "Inode numbers on hard-linked files are different" fi rm -rf tmp # wimsplit, wimjoin echo "Creating random files to test WIM splitting on" mkdir tmp for ((i = 0; i < 100; i++)); do dd if=/dev/urandom of=tmp/file$i bs=4096 count=10 &> /dev/null done for flag in "--compress=none" "--compress=maximum" "--compress=fast"; do echo "Using flag $flag" if ! wimcapture tmp tmp.wim $flag; then error "Failed to capture test WIM" fi echo "Splitting WIM into 1 MiB chunks" if ! wimsplit tmp.wim tmp.swm 1; then error "Failed to split WIM" fi echo "Verifying the split WIMs (some errors expected)" if test "`wiminfo tmp.swm | grep 'Part Number' | awk '{print $3}'`" != "1/4"; then error "Part number of split WIM not correct" fi if ! wimdir tmp.swm > /dev/null; then error "Failed to list files in split WIM" fi if ! test -e tmp2.swm; then error "Could not find split-WIM part 2" fi if wimdir tmp2.swm > /dev/null; then error "Listed files in part 2 of split WIM (this should have failed)" fi # Unsupported, should fail if wiminfo tmp.swm --boot 1; then error "Should not have been able to change boot index of split WIM" fi echo "Joining the split WIMs and applying the result" if ! wimjoin tmp2.wim tmp*.wim; then error "Failed to join split WIMs" fi if ! wimapply tmp2.wim tmp2; then error "Failed to apply joined split WIM" fi if ! wimapply tmp.wim tmp3; then error "Failed to apply test WIM" fi if ! diff -q -r tmp tmp2 || ! diff -q -r tmp tmp3; then error "Recursive diff of applied joined split WIM with original directory failed" fi rm -f *.wim *.swm rm -rf tmp2 tmp3 done rm -rf tmp # wimexport echo "Testing export of single image to new WIM" if ! wimcapture dir dir.wim; then error "Failed to capture test WIM" fi if ! wimappend dir2 dir.wim; then error "Failed to append image to test WIM" fi if ! wimexport dir.wim dir new.wim; then error "Failed to export single image to new WIM" fi if test "`wiminfo new.wim | grep 'Image Count' | awk '{print $3}'`" != 1; then error "Exporting single image to new WIM wasn't done correctly" fi echo "Testing export of single image to existing WIM" if ! wimexport dir.wim dir2 new.wim; then error "Failed to export single image to existing WIM" fi if test "`wiminfo new.wim | grep 'Image Count' | awk '{print $3}'`" != 2; then error "Exporting single image to existing WIM wasn't done correctly" fi echo "Testing export of single image to existing WIM using wrong compression type" if wimexport dir.wim dir2 new.wim newname --compress=xpress; then error "Successfully exported image using wrong compression type" fi rm -f new.wim echo "Testing export of multiple images to new WIM" if ! wimexport dir.wim all new.wim; then error "Failed to export multiple images to new WIM" fi if test "`wiminfo new.wim | grep 'Image Count' | awk '{print $3}'`" != 2; then error "Exporting multiple images to new WIM wasn't done correctly" fi if ! wimcapture dir2 new.wim newname; then error "Failed to capture test WIM" fi echo "Testing export of multiple images to existing WIM" if ! wimexport dir.wim all new.wim; then error "Failed to export multiple images to existing WIM" fi echo "Testing export of multiple images to existing WIM with --boot" if ! wimcapture dir2 new.wim newname; then error "Failed to capture test WIM" fi if ! wiminfo dir.wim --boot 1; then error "Failed to set boot index on test WIM" fi if ! wimexport dir.wim all new.wim --boot; then error "Failed to export multiple images to existing WIM with bootable image" fi echo "Testing export of multiple images to existing WIM with --boot, but no bootable image (errors expected)" if ! wimcapture dir2 new.wim newname; then error "Failed to capture test WIM" fi if ! wiminfo dir.wim --boot 0; then error "Failed to clear boot index on test WIM" fi if wimexport dir.wim all new.wim --boot; then error "Successfully exported multiple images with --boot but with no bootable images" fi # Test exporting an image to another WIM, then applying it. # We try with 5 different combinations of compression types to make sure we go # through all paths in the resource-handling code. for i in 1 2 3 4 5; do case $i in 1) cflag1="--compress=none"; cflag2="--compress=none"; ;; 2) cflag1="--compress=xpress"; cflag2="--compress=xpress"; ;; 3) cflag1="--compress=xpress" cflag2="--compress=lzx" ;; 4) cflag1="--compress=none" cflag2="--compress=xpress" ;; 5) cflag1="--compress=xpress" cflag2="--compress=none" ;; esac echo "Testing exporting then applying an image (\"$cflag1\" => \"$cflag2\")" rm -rf dir.wim new.wim tmp tmp2 wimcapture dir dir.wim $cflag1 wimcapture dir2 dir2.wim $cflag2 wimexport dir.wim dir dir2.wim wimapply dir.wim dir tmp if ! wimapply dir2.wim dir tmp2; then error "Failed to apply image that was exported to a WIM" fi if ! diff -r tmp tmp2; then error "Image that was exported to a WIM was not applied correctly" fi done echo "**********************************************************" echo " Basic wimlib-imagex tests passed " echo "**********************************************************" # Leave test subdirectory and cleanup cd .. default_cleanup wimlib-1.13.1/COPYING.CC00000644000175000017500000001561013160354224011403 00000000000000Creative Commons Legal Code CC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; ii. moral rights retained by the original author(s) and/or performer(s); iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; v. rights protecting the extraction, dissemination, use and reuse of data in a Work; vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. wimlib-1.13.1/Makefile.in0000644000175000017500000054062313464166627012077 00000000000000# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ ############################################################################## # General # ############################################################################## VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @WITH_NTFS_3G_TRUE@am__append_1 = src/ntfs-3g_apply.c \ @WITH_NTFS_3G_TRUE@ src/ntfs-3g_capture.c \ @WITH_NTFS_3G_TRUE@ include/wimlib/ntfs_3g.h @WINDOWS_NATIVE_BUILD_TRUE@am__append_2 = src/wimboot.c \ @WINDOWS_NATIVE_BUILD_TRUE@ src/win32_common.c \ @WINDOWS_NATIVE_BUILD_TRUE@ src/win32_apply.c \ @WINDOWS_NATIVE_BUILD_TRUE@ src/win32_capture.c \ @WINDOWS_NATIVE_BUILD_TRUE@ src/win32_replacements.c \ @WINDOWS_NATIVE_BUILD_TRUE@ src/win32_vss.c \ @WINDOWS_NATIVE_BUILD_TRUE@ include/wimlib/wimboot.h \ @WINDOWS_NATIVE_BUILD_TRUE@ include/wimlib/win32.h \ @WINDOWS_NATIVE_BUILD_TRUE@ include/wimlib/win32_common.h \ @WINDOWS_NATIVE_BUILD_TRUE@ include/wimlib/win32_vss.h \ @WINDOWS_NATIVE_BUILD_TRUE@ include/wimlib/wof.h @WINDOWS_NATIVE_BUILD_FALSE@am__append_3 = src/unix_apply.c \ @WINDOWS_NATIVE_BUILD_FALSE@ src/unix_capture.c @ENABLE_TEST_SUPPORT_TRUE@am__append_4 = src/test_support.c \ @ENABLE_TEST_SUPPORT_TRUE@ include/wimlib/test_support.h # Workaround for "multiple definition" error when math symbols are present in # both libmsvcrt.a and ntdll.a @ENABLE_TEST_SUPPORT_TRUE@@WINDOWS_NATIVE_BUILD_TRUE@am__append_5 = -Wl,--allow-multiple-definition @ENABLE_SSSE3_SHA1_TRUE@am__append_6 = src/sha1-ssse3.asm @ENABLE_SSSE3_SHA1_TRUE@am__append_7 = src/sha1-ssse3.lo bin_PROGRAMS = wimlib-imagex$(EXEEXT) @WINDOWS_NATIVE_BUILD_TRUE@am__append_8 = \ @WINDOWS_NATIVE_BUILD_TRUE@ programs/imagex-win32.c \ @WINDOWS_NATIVE_BUILD_TRUE@ programs/imagex-win32.h \ @WINDOWS_NATIVE_BUILD_TRUE@ programs/wgetopt.c \ @WINDOWS_NATIVE_BUILD_TRUE@ programs/wgetopt.h check_PROGRAMS = tests/tree-cmp$(EXEEXT) @WITH_FUSE_TRUE@am__append_9 = tests/test-imagex-mount @WITH_NTFS_3G_TRUE@am__append_10 = tests/test-imagex-ntfs @WINDOWS_NATIVE_BUILD_FALSE@TESTS = $(dist_check_SCRIPTS) EXTRA_PROGRAMS = tests/wlfuzz$(EXEEXT) subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nasm.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ $(am__configure_deps) $(dist_bin_SCRIPTS) \ $(am__dist_check_SCRIPTS_DIST) $(include_HEADERS) \ $(am__DIST_COMMON) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d CONFIG_HEADER = config.h CONFIG_CLEAN_FILES = doc/Doxyfile wimlib.pc programs/mkwinpeimg CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" \ "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" \ "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(includedir)" PROGRAMS = $(bin_PROGRAMS) am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } LTLIBRARIES = $(lib_LTLIBRARIES) am__DEPENDENCIES_1 = libwim_la_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__append_7) am__libwim_la_SOURCES_DIST = src/add_image.c src/avl_tree.c \ src/blob_table.c src/compress.c src/compress_common.c \ src/compress_parallel.c src/compress_serial.c src/decompress.c \ src/decompress_common.c src/delete_image.c src/dentry.c \ src/divsufsort.c src/encoding.c src/error.c src/export_image.c \ src/extract.c src/file_io.c src/header.c src/inode.c \ src/inode_fixup.c src/inode_table.c src/integrity.c \ src/iterate_dir.c src/join.c src/lcpit_matchfinder.c \ src/lzms_common.c src/lzms_compress.c src/lzms_decompress.c \ src/lzx_common.c src/lzx_compress.c src/lzx_decompress.c \ src/metadata_resource.c src/mount_image.c src/pathlist.c \ src/paths.c src/pattern.c src/progress.c src/reference.c \ src/registry.c src/reparse.c src/resource.c src/scan.c \ src/security.c src/sha1.c src/solid.c src/split.c \ src/tagged_items.c src/template.c src/textfile.c \ src/timestamp.c src/update_image.c src/util.c src/verify.c \ src/wim.c src/write.c src/x86_cpu_features.c src/xml.c \ src/xml_windows.c src/xpress_compress.c \ src/xpress_decompress.c include/wimlib/alloca.h \ include/wimlib/apply.h include/wimlib/assert.h \ include/wimlib/avl_tree.h include/wimlib/bitops.h \ include/wimlib/blob_table.h include/wimlib/bt_matchfinder.h \ include/wimlib/case.h include/wimlib/compiler.h \ include/wimlib/compressor_ops.h \ include/wimlib/compress_common.h \ include/wimlib/chunk_compressor.h \ include/wimlib/decompressor_ops.h \ include/wimlib/decompress_common.h include/wimlib/dentry.h \ include/wimlib/divsufsort.h include/wimlib/encoding.h \ include/wimlib/endianness.h include/wimlib/error.h \ include/wimlib/file_io.h include/wimlib/glob.h \ include/wimlib/guid.h include/wimlib/hc_matchfinder.h \ include/wimlib/header.h include/wimlib/inode.h \ include/wimlib/inode_table.h include/wimlib/integrity.h \ include/wimlib/lcpit_matchfinder.h include/wimlib/list.h \ include/wimlib/lz_extend.h include/wimlib/lz_hash.h \ include/wimlib/lzms_common.h include/wimlib/lzms_constants.h \ include/wimlib/lzx_common.h include/wimlib/lzx_constants.h \ include/wimlib/metadata.h include/wimlib/object_id.h \ include/wimlib/pathlist.h include/wimlib/paths.h \ include/wimlib/pattern.h include/wimlib/progress.h \ include/wimlib/registry.h include/wimlib/reparse.h \ include/wimlib/resource.h include/wimlib/scan.h \ include/wimlib/security.h include/wimlib/security_descriptor.h \ include/wimlib/sha1.h include/wimlib/solid.h \ include/wimlib/tagged_items.h include/wimlib/textfile.h \ include/wimlib/timestamp.h include/wimlib/types.h \ include/wimlib/unaligned.h include/wimlib/unix_data.h \ include/wimlib/util.h include/wimlib/wim.h \ include/wimlib/write.h include/wimlib/x86_cpu_features.h \ include/wimlib/xattr.h include/wimlib/xml.h \ include/wimlib/xml_windows.h include/wimlib/xpress_constants.h \ src/ntfs-3g_apply.c src/ntfs-3g_capture.c \ include/wimlib/ntfs_3g.h src/wimboot.c src/win32_common.c \ src/win32_apply.c src/win32_capture.c src/win32_replacements.c \ src/win32_vss.c include/wimlib/wimboot.h \ include/wimlib/win32.h include/wimlib/win32_common.h \ include/wimlib/win32_vss.h include/wimlib/wof.h \ src/unix_apply.c src/unix_capture.c src/test_support.c \ include/wimlib/test_support.h src/sha1-ssse3.asm am__dirstamp = $(am__leading_dot)dirstamp @WITH_NTFS_3G_TRUE@am__objects_1 = src/libwim_la-ntfs-3g_apply.lo \ @WITH_NTFS_3G_TRUE@ src/libwim_la-ntfs-3g_capture.lo @WINDOWS_NATIVE_BUILD_TRUE@am__objects_2 = src/libwim_la-wimboot.lo \ @WINDOWS_NATIVE_BUILD_TRUE@ src/libwim_la-win32_common.lo \ @WINDOWS_NATIVE_BUILD_TRUE@ src/libwim_la-win32_apply.lo \ @WINDOWS_NATIVE_BUILD_TRUE@ src/libwim_la-win32_capture.lo \ @WINDOWS_NATIVE_BUILD_TRUE@ src/libwim_la-win32_replacements.lo \ @WINDOWS_NATIVE_BUILD_TRUE@ src/libwim_la-win32_vss.lo @WINDOWS_NATIVE_BUILD_FALSE@am__objects_3 = \ @WINDOWS_NATIVE_BUILD_FALSE@ src/libwim_la-unix_apply.lo \ @WINDOWS_NATIVE_BUILD_FALSE@ src/libwim_la-unix_capture.lo @ENABLE_TEST_SUPPORT_TRUE@am__objects_4 = \ @ENABLE_TEST_SUPPORT_TRUE@ src/libwim_la-test_support.lo am__objects_5 = am_libwim_la_OBJECTS = src/libwim_la-add_image.lo \ src/libwim_la-avl_tree.lo src/libwim_la-blob_table.lo \ src/libwim_la-compress.lo src/libwim_la-compress_common.lo \ src/libwim_la-compress_parallel.lo \ src/libwim_la-compress_serial.lo src/libwim_la-decompress.lo \ src/libwim_la-decompress_common.lo \ src/libwim_la-delete_image.lo src/libwim_la-dentry.lo \ src/libwim_la-divsufsort.lo src/libwim_la-encoding.lo \ src/libwim_la-error.lo src/libwim_la-export_image.lo \ src/libwim_la-extract.lo src/libwim_la-file_io.lo \ src/libwim_la-header.lo src/libwim_la-inode.lo \ src/libwim_la-inode_fixup.lo src/libwim_la-inode_table.lo \ src/libwim_la-integrity.lo src/libwim_la-iterate_dir.lo \ src/libwim_la-join.lo src/libwim_la-lcpit_matchfinder.lo \ src/libwim_la-lzms_common.lo src/libwim_la-lzms_compress.lo \ src/libwim_la-lzms_decompress.lo src/libwim_la-lzx_common.lo \ src/libwim_la-lzx_compress.lo src/libwim_la-lzx_decompress.lo \ src/libwim_la-metadata_resource.lo \ src/libwim_la-mount_image.lo src/libwim_la-pathlist.lo \ src/libwim_la-paths.lo src/libwim_la-pattern.lo \ src/libwim_la-progress.lo src/libwim_la-reference.lo \ src/libwim_la-registry.lo src/libwim_la-reparse.lo \ src/libwim_la-resource.lo src/libwim_la-scan.lo \ src/libwim_la-security.lo src/libwim_la-sha1.lo \ src/libwim_la-solid.lo src/libwim_la-split.lo \ src/libwim_la-tagged_items.lo src/libwim_la-template.lo \ src/libwim_la-textfile.lo src/libwim_la-timestamp.lo \ src/libwim_la-update_image.lo src/libwim_la-util.lo \ src/libwim_la-verify.lo src/libwim_la-wim.lo \ src/libwim_la-write.lo src/libwim_la-x86_cpu_features.lo \ src/libwim_la-xml.lo src/libwim_la-xml_windows.lo \ src/libwim_la-xpress_compress.lo \ src/libwim_la-xpress_decompress.lo $(am__objects_1) \ $(am__objects_2) $(am__objects_3) $(am__objects_4) \ $(am__objects_5) libwim_la_OBJECTS = $(am_libwim_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libwim_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(libwim_la_CFLAGS) \ $(CFLAGS) $(libwim_la_LDFLAGS) $(LDFLAGS) -o $@ am_tests_tree_cmp_OBJECTS = tests/tree-cmp.$(OBJEXT) tests_tree_cmp_OBJECTS = $(am_tests_tree_cmp_OBJECTS) tests_tree_cmp_LDADD = $(LDADD) am_tests_wlfuzz_OBJECTS = tests/wlfuzz.$(OBJEXT) tests_wlfuzz_OBJECTS = $(am_tests_wlfuzz_OBJECTS) tests_wlfuzz_DEPENDENCIES = $(top_builddir)/libwim.la am__wimlib_imagex_SOURCES_DIST = programs/imagex.c include/wimlib.h \ include/wimlib_tchar.h programs/imagex-win32.c \ programs/imagex-win32.h programs/wgetopt.c programs/wgetopt.h @WINDOWS_NATIVE_BUILD_TRUE@am__objects_6 = programs/wimlib_imagex-imagex-win32.$(OBJEXT) \ @WINDOWS_NATIVE_BUILD_TRUE@ programs/wimlib_imagex-wgetopt.$(OBJEXT) am_wimlib_imagex_OBJECTS = programs/wimlib_imagex-imagex.$(OBJEXT) \ $(am__objects_6) wimlib_imagex_OBJECTS = $(am_wimlib_imagex_OBJECTS) wimlib_imagex_DEPENDENCIES = $(top_builddir)/libwim.la wimlib_imagex_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(wimlib_imagex_CFLAGS) \ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ am__dist_check_SCRIPTS_DIST = tests/test-imagex \ tests/test-imagex-capture_and_apply \ tests/test-imagex-update_and_extract tests/test-imagex-mount \ tests/test-imagex-ntfs SCRIPTS = $(dist_bin_SCRIPTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = \ programs/$(DEPDIR)/wimlib_imagex-imagex-win32.Po \ programs/$(DEPDIR)/wimlib_imagex-imagex.Po \ programs/$(DEPDIR)/wimlib_imagex-wgetopt.Po \ src/$(DEPDIR)/libwim_la-add_image.Plo \ src/$(DEPDIR)/libwim_la-avl_tree.Plo \ src/$(DEPDIR)/libwim_la-blob_table.Plo \ src/$(DEPDIR)/libwim_la-compress.Plo \ src/$(DEPDIR)/libwim_la-compress_common.Plo \ src/$(DEPDIR)/libwim_la-compress_parallel.Plo \ src/$(DEPDIR)/libwim_la-compress_serial.Plo \ src/$(DEPDIR)/libwim_la-decompress.Plo \ src/$(DEPDIR)/libwim_la-decompress_common.Plo \ src/$(DEPDIR)/libwim_la-delete_image.Plo \ src/$(DEPDIR)/libwim_la-dentry.Plo \ src/$(DEPDIR)/libwim_la-divsufsort.Plo \ src/$(DEPDIR)/libwim_la-encoding.Plo \ src/$(DEPDIR)/libwim_la-error.Plo \ src/$(DEPDIR)/libwim_la-export_image.Plo \ src/$(DEPDIR)/libwim_la-extract.Plo \ src/$(DEPDIR)/libwim_la-file_io.Plo \ src/$(DEPDIR)/libwim_la-header.Plo \ src/$(DEPDIR)/libwim_la-inode.Plo \ src/$(DEPDIR)/libwim_la-inode_fixup.Plo \ src/$(DEPDIR)/libwim_la-inode_table.Plo \ src/$(DEPDIR)/libwim_la-integrity.Plo \ src/$(DEPDIR)/libwim_la-iterate_dir.Plo \ src/$(DEPDIR)/libwim_la-join.Plo \ src/$(DEPDIR)/libwim_la-lcpit_matchfinder.Plo \ src/$(DEPDIR)/libwim_la-lzms_common.Plo \ src/$(DEPDIR)/libwim_la-lzms_compress.Plo \ src/$(DEPDIR)/libwim_la-lzms_decompress.Plo \ src/$(DEPDIR)/libwim_la-lzx_common.Plo \ src/$(DEPDIR)/libwim_la-lzx_compress.Plo \ src/$(DEPDIR)/libwim_la-lzx_decompress.Plo \ src/$(DEPDIR)/libwim_la-metadata_resource.Plo \ src/$(DEPDIR)/libwim_la-mount_image.Plo \ src/$(DEPDIR)/libwim_la-ntfs-3g_apply.Plo \ src/$(DEPDIR)/libwim_la-ntfs-3g_capture.Plo \ src/$(DEPDIR)/libwim_la-pathlist.Plo \ src/$(DEPDIR)/libwim_la-paths.Plo \ src/$(DEPDIR)/libwim_la-pattern.Plo \ src/$(DEPDIR)/libwim_la-progress.Plo \ src/$(DEPDIR)/libwim_la-reference.Plo \ src/$(DEPDIR)/libwim_la-registry.Plo \ src/$(DEPDIR)/libwim_la-reparse.Plo \ src/$(DEPDIR)/libwim_la-resource.Plo \ src/$(DEPDIR)/libwim_la-scan.Plo \ src/$(DEPDIR)/libwim_la-security.Plo \ src/$(DEPDIR)/libwim_la-sha1.Plo \ src/$(DEPDIR)/libwim_la-solid.Plo \ src/$(DEPDIR)/libwim_la-split.Plo \ src/$(DEPDIR)/libwim_la-tagged_items.Plo \ src/$(DEPDIR)/libwim_la-template.Plo \ src/$(DEPDIR)/libwim_la-test_support.Plo \ src/$(DEPDIR)/libwim_la-textfile.Plo \ src/$(DEPDIR)/libwim_la-timestamp.Plo \ src/$(DEPDIR)/libwim_la-unix_apply.Plo \ src/$(DEPDIR)/libwim_la-unix_capture.Plo \ src/$(DEPDIR)/libwim_la-update_image.Plo \ src/$(DEPDIR)/libwim_la-util.Plo \ src/$(DEPDIR)/libwim_la-verify.Plo \ src/$(DEPDIR)/libwim_la-wim.Plo \ src/$(DEPDIR)/libwim_la-wimboot.Plo \ src/$(DEPDIR)/libwim_la-win32_apply.Plo \ src/$(DEPDIR)/libwim_la-win32_capture.Plo \ src/$(DEPDIR)/libwim_la-win32_common.Plo \ src/$(DEPDIR)/libwim_la-win32_replacements.Plo \ src/$(DEPDIR)/libwim_la-win32_vss.Plo \ src/$(DEPDIR)/libwim_la-write.Plo \ src/$(DEPDIR)/libwim_la-x86_cpu_features.Plo \ src/$(DEPDIR)/libwim_la-xml.Plo \ src/$(DEPDIR)/libwim_la-xml_windows.Plo \ src/$(DEPDIR)/libwim_la-xpress_compress.Plo \ src/$(DEPDIR)/libwim_la-xpress_decompress.Plo \ tests/$(DEPDIR)/tree-cmp.Po tests/$(DEPDIR)/wlfuzz.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libwim_la_SOURCES) $(tests_tree_cmp_SOURCES) \ $(tests_wlfuzz_SOURCES) $(wimlib_imagex_SOURCES) DIST_SOURCES = $(am__libwim_la_SOURCES_DIST) $(tests_tree_cmp_SOURCES) \ $(tests_wlfuzz_SOURCES) $(am__wimlib_imagex_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac man1dir = $(mandir)/man1 NROFF = nroff MANS = $(man1_MANS) DATA = $(pkgconfig_DATA) HEADERS = $(include_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ $(LISP)config.h.in # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags CSCOPE = cscope AM_RECURSIVE_TARGETS = cscope check recheck am__tty_colors_dummy = \ mgn= red= grn= lgn= blu= brg= std=; \ am__color_tests=no am__tty_colors = { \ $(am__tty_colors_dummy); \ if test "X$(AM_COLOR_TESTS)" = Xno; then \ am__color_tests=no; \ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ am__color_tests=yes; \ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ am__color_tests=yes; \ fi; \ if test $$am__color_tests = yes; then \ red=''; \ grn=''; \ lgn=''; \ blu=''; \ mgn=''; \ brg=''; \ std=''; \ fi; \ } am__recheck_rx = ^[ ]*:recheck:[ ]* am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* # A command that, given a newline-separated list of test names on the # standard input, print the name of the tests that are to be re-run # upon "make recheck". am__list_recheck_tests = $(AWK) '{ \ recheck = 1; \ while ((rc = (getline line < ($$0 ".trs"))) != 0) \ { \ if (rc < 0) \ { \ if ((getline line2 < ($$0 ".log")) < 0) \ recheck = 0; \ break; \ } \ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ { \ recheck = 0; \ break; \ } \ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ { \ break; \ } \ }; \ if (recheck) \ print $$0; \ close ($$0 ".trs"); \ close ($$0 ".log"); \ }' # A command that, given a newline-separated list of test names on the # standard input, create the global log from their .trs and .log files. am__create_global_log = $(AWK) ' \ function fatal(msg) \ { \ print "fatal: making $@: " msg | "cat >&2"; \ exit 1; \ } \ function rst_section(header) \ { \ print header; \ len = length(header); \ for (i = 1; i <= len; i = i + 1) \ printf "="; \ printf "\n\n"; \ } \ { \ copy_in_global_log = 1; \ global_test_result = "RUN"; \ while ((rc = (getline line < ($$0 ".trs"))) != 0) \ { \ if (rc < 0) \ fatal("failed to read from " $$0 ".trs"); \ if (line ~ /$(am__global_test_result_rx)/) \ { \ sub("$(am__global_test_result_rx)", "", line); \ sub("[ ]*$$", "", line); \ global_test_result = line; \ } \ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ copy_in_global_log = 0; \ }; \ if (copy_in_global_log) \ { \ rst_section(global_test_result ": " $$0); \ while ((rc = (getline line < ($$0 ".log"))) != 0) \ { \ if (rc < 0) \ fatal("failed to read from " $$0 ".log"); \ print line; \ }; \ printf "\n"; \ }; \ close ($$0 ".trs"); \ close ($$0 ".log"); \ }' # Restructured Text title. am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } # Solaris 10 'make', and several other traditional 'make' implementations, # pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it # by disabling -e (using the XSI extension "set +e") if it's set. am__sh_e_setup = case $$- in *e*) set +e;; esac # Default flags passed to test drivers. am__common_driver_flags = \ --color-tests "$$am__color_tests" \ --enable-hard-errors "$$am__enable_hard_errors" \ --expect-failure "$$am__expect_failure" # To be inserted before the command running the test. Creates the # directory for the log if needed. Stores in $dir the directory # containing $f, in $tst the test, in $log the log. Executes the # developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and # passes TESTS_ENVIRONMENT. Set up options for the wrapper that # will run the test scripts (or their associated LOG_COMPILER, if # thy have one). am__check_pre = \ $(am__sh_e_setup); \ $(am__vpath_adj_setup) $(am__vpath_adj) \ $(am__tty_colors); \ srcdir=$(srcdir); export srcdir; \ case "$@" in \ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ *) am__odir=.;; \ esac; \ test "x$$am__odir" = x"." || test -d "$$am__odir" \ || $(MKDIR_P) "$$am__odir" || exit $$?; \ if test -f "./$$f"; then dir=./; \ elif test -f "$$f"; then dir=; \ else dir="$(srcdir)/"; fi; \ tst=$$dir$$f; log='$@'; \ if test -n '$(DISABLE_HARD_ERRORS)'; then \ am__enable_hard_errors=no; \ else \ am__enable_hard_errors=yes; \ fi; \ case " $(XFAIL_TESTS) " in \ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ am__expect_failure=yes;; \ *) \ am__expect_failure=no;; \ esac; \ $(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) # A shell command to get the names of the tests scripts with any registered # extension removed (i.e., equivalently, the names of the test logs, with # the '.log' extension removed). The result is saved in the shell variable # '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, # we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", # since that might cause problem with VPATH rewrites for suffix-less tests. # See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. am__set_TESTS_bases = \ bases='$(TEST_LOGS)'; \ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ bases=`echo $$bases` RECHECK_LOGS = $(TEST_LOGS) TEST_SUITE_LOG = test-suite.log TEST_EXTENSIONS = @EXEEXT@ .test LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) am__set_b = \ case '$@' in \ */*) \ case '$*' in \ */*) b='$*';; \ *) b=`echo '$@' | sed 's/\.log$$//'`; \ esac;; \ *) \ b='$*';; \ esac am__test_logs1 = $(TESTS:=.log) am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) TEST_LOGS = $(am__test_logs2:.test.log=.log) TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ $(TEST_LOG_FLAGS) am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \ $(srcdir)/wimlib.pc.in $(top_srcdir)/build-aux/ar-lib \ $(top_srcdir)/build-aux/compile \ $(top_srcdir)/build-aux/config.guess \ $(top_srcdir)/build-aux/config.sub \ $(top_srcdir)/build-aux/depcomp \ $(top_srcdir)/build-aux/install-sh \ $(top_srcdir)/build-aux/ltmain.sh \ $(top_srcdir)/build-aux/missing \ $(top_srcdir)/build-aux/test-driver \ $(top_srcdir)/doc/Doxyfile.in \ $(top_srcdir)/programs/mkwinpeimg.in COPYING INSTALL NEWS \ README build-aux/ar-lib build-aux/compile \ build-aux/config.guess build-aux/config.rpath \ build-aux/config.sub build-aux/depcomp build-aux/install-sh \ build-aux/ltmain.sh build-aux/missing DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) am__remove_distdir = \ if test -d "$(distdir)"; then \ find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ && rm -rf "$(distdir)" \ || { sleep 5 && rm -rf "$(distdir)"; }; \ else :; fi am__post_remove_distdir = $(am__remove_distdir) DIST_ARCHIVES = $(distdir).tar.gz GZIP_ENV = --best DIST_TARGETS = dist-gzip distuninstallcheck_listfiles = find . -type f -print am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' distcleancheck_listfiles = find . -type f -print ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBCRYPTO_CFLAGS = @LIBCRYPTO_CFLAGS@ LIBCRYPTO_LIBS = @LIBCRYPTO_LIBS@ LIBFUSE_CFLAGS = @LIBFUSE_CFLAGS@ LIBFUSE_LIBS = @LIBFUSE_LIBS@ LIBNTFS_3G_CFLAGS = @LIBNTFS_3G_CFLAGS@ LIBNTFS_3G_LIBS = @LIBNTFS_3G_LIBS@ LIBOBJS = @LIBOBJS@ LIBRT_LIBS = @LIBRT_LIBS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ LIBXML2_LIBS = @LIBXML2_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NAFLAGS = @NAFLAGS@ NASM = @NASM@ NASM_PLATFORM_FLAGS = @NASM_PLATFORM_FLAGS@ NASM_SYMBOL_PREFIX = @NASM_SYMBOL_PREFIX@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKGCONFIG_PRIVATE_LIBS = @PKGCONFIG_PRIVATE_LIBS@ PKGCONFIG_PRIVATE_REQUIRES = @PKGCONFIG_PRIVATE_REQUIRES@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PLATFORM_CFLAGS = @PLATFORM_CFLAGS@ PLATFORM_CPPFLAGS = @PLATFORM_CPPFLAGS@ PLATFORM_LDFLAGS = @PLATFORM_LDFLAGS@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ ACLOCAL_AMFLAGS = -I m4 AM_CPPFLAGS = -I$(top_srcdir)/include $(PLATFORM_CPPFLAGS) \ -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE # Note: -std=gnu99 instead of -std=c99 is needed for unnamed structs and unions, # which are in C11 but not C99. But we can't yet actually use -std=c11 because # we want to support older versions of gcc. AM_CFLAGS = -std=gnu99 $(PLATFORM_CFLAGS) -fno-common \ -Wmissing-prototypes -Wstrict-prototypes \ -Wundef -Wno-pointer-sign AM_LDFLAGS = $(PLATFORM_LDFLAGS) $(am__append_5) EXTRA_DIST = README README.WINDOWS COPYING COPYING.GPLv3 \ COPYING.LGPLv3 COPYING.CC0 examples debian rpm \ build-aux/nasm_lt.sh $(man1_MANS) tests/common_tests.sh \ tests/exclusionlists tests/test_utils.sh \ tests/security_descriptor_1.base64 \ tests/security_descriptor_1.bin \ tests/security_descriptor_2.base64 \ tests/security_descriptor_2.bin ############################################################################## # Library # ############################################################################## lib_LTLIBRARIES = libwim.la include_HEADERS = include/wimlib.h pkgconfig_DATA = wimlib.pc libwim_la_SOURCES = src/add_image.c src/avl_tree.c src/blob_table.c \ src/compress.c src/compress_common.c src/compress_parallel.c \ src/compress_serial.c src/decompress.c src/decompress_common.c \ src/delete_image.c src/dentry.c src/divsufsort.c \ src/encoding.c src/error.c src/export_image.c src/extract.c \ src/file_io.c src/header.c src/inode.c src/inode_fixup.c \ src/inode_table.c src/integrity.c src/iterate_dir.c src/join.c \ src/lcpit_matchfinder.c src/lzms_common.c src/lzms_compress.c \ src/lzms_decompress.c src/lzx_common.c src/lzx_compress.c \ src/lzx_decompress.c src/metadata_resource.c src/mount_image.c \ src/pathlist.c src/paths.c src/pattern.c src/progress.c \ src/reference.c src/registry.c src/reparse.c src/resource.c \ src/scan.c src/security.c src/sha1.c src/solid.c src/split.c \ src/tagged_items.c src/template.c src/textfile.c \ src/timestamp.c src/update_image.c src/util.c src/verify.c \ src/wim.c src/write.c src/x86_cpu_features.c src/xml.c \ src/xml_windows.c src/xpress_compress.c \ src/xpress_decompress.c include/wimlib/alloca.h \ include/wimlib/apply.h include/wimlib/assert.h \ include/wimlib/avl_tree.h include/wimlib/bitops.h \ include/wimlib/blob_table.h include/wimlib/bt_matchfinder.h \ include/wimlib/case.h include/wimlib/compiler.h \ include/wimlib/compressor_ops.h \ include/wimlib/compress_common.h \ include/wimlib/chunk_compressor.h \ include/wimlib/decompressor_ops.h \ include/wimlib/decompress_common.h include/wimlib/dentry.h \ include/wimlib/divsufsort.h include/wimlib/encoding.h \ include/wimlib/endianness.h include/wimlib/error.h \ include/wimlib/file_io.h include/wimlib/glob.h \ include/wimlib/guid.h include/wimlib/hc_matchfinder.h \ include/wimlib/header.h include/wimlib/inode.h \ include/wimlib/inode_table.h include/wimlib/integrity.h \ include/wimlib/lcpit_matchfinder.h include/wimlib/list.h \ include/wimlib/lz_extend.h include/wimlib/lz_hash.h \ include/wimlib/lzms_common.h include/wimlib/lzms_constants.h \ include/wimlib/lzx_common.h include/wimlib/lzx_constants.h \ include/wimlib/metadata.h include/wimlib/object_id.h \ include/wimlib/pathlist.h include/wimlib/paths.h \ include/wimlib/pattern.h include/wimlib/progress.h \ include/wimlib/registry.h include/wimlib/reparse.h \ include/wimlib/resource.h include/wimlib/scan.h \ include/wimlib/security.h include/wimlib/security_descriptor.h \ include/wimlib/sha1.h include/wimlib/solid.h \ include/wimlib/tagged_items.h include/wimlib/textfile.h \ include/wimlib/timestamp.h include/wimlib/types.h \ include/wimlib/unaligned.h include/wimlib/unix_data.h \ include/wimlib/util.h include/wimlib/wim.h \ include/wimlib/write.h include/wimlib/x86_cpu_features.h \ include/wimlib/xattr.h include/wimlib/xml.h \ include/wimlib/xml_windows.h include/wimlib/xpress_constants.h \ $(am__append_1) $(am__append_2) $(am__append_3) \ $(am__append_4) $(am__append_6) @WINDOWS_NATIVE_BUILD_FALSE@PLATFORM_LIBS = @WINDOWS_NATIVE_BUILD_TRUE@PLATFORM_LIBS = -lmsvcrt -lntdll libwim_la_CFLAGS = \ $(AM_CFLAGS) \ $(PTHREAD_CFLAGS) \ $(LIBXML2_CFLAGS) \ $(LIBNTFS_3G_CFLAGS) \ $(LIBFUSE_CFLAGS) \ $(LIBCRYPTO_CFLAGS) libwim_la_LDFLAGS = $(AM_LDFLAGS) -version-info 31:0:16 libwim_la_LIBADD = $(PTHREAD_LIBS) $(LIBXML2_LIBS) $(LIBNTFS_3G_LIBS) \ $(LIBFUSE_LIBS) $(LIBRT_LIBS) $(LIBCRYPTO_LIBS) \ $(PLATFORM_LIBS) $(am__append_7) dist_bin_SCRIPTS = programs/mkwinpeimg wimlib_imagex_SOURCES = programs/imagex.c include/wimlib.h \ include/wimlib_tchar.h $(am__append_8) wimlib_imagex_CFLAGS = $(AM_CFLAGS) -Wno-deprecated-declarations wimlib_imagex_LDADD = $(top_builddir)/libwim.la wimlib_imagex_cmds = \ append \ apply \ capture \ delete \ dir \ export \ extract \ info \ join \ mount \ mountrw \ optimize \ split \ unmount \ update \ verify ############################################################################## # Documentation # ############################################################################## man1_MANS = \ doc/man1/wimlib-imagex.1 \ doc/man1/wimappend.1 \ doc/man1/wimapply.1 \ doc/man1/wimcapture.1 \ doc/man1/wimdelete.1 \ doc/man1/wimdir.1 \ doc/man1/wimexport.1 \ doc/man1/wimextract.1 \ doc/man1/wiminfo.1 \ doc/man1/wimjoin.1 \ doc/man1/wimmount.1 \ doc/man1/wimmountrw.1 \ doc/man1/wimoptimize.1 \ doc/man1/wimsplit.1 \ doc/man1/wimunmount.1 \ doc/man1/wimupdate.1 \ doc/man1/wimverify.1 \ doc/man1/mkwinpeimg.1 tests_tree_cmp_SOURCES = tests/tree-cmp.c dist_check_SCRIPTS = tests/test-imagex \ tests/test-imagex-capture_and_apply \ tests/test-imagex-update_and_extract $(am__append_9) \ $(am__append_10) tests_wlfuzz_SOURCES = tests/wlfuzz.c tests_wlfuzz_LDADD = $(top_builddir)/libwim.la all: config.h $(MAKE) $(AM_MAKEFLAGS) all-am .SUFFIXES: .SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs am--refresh: Makefile @: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck $(top_srcdir)/configure: $(am__configure_deps) $(am__cd) $(srcdir) && $(AUTOCONF) $(ACLOCAL_M4): $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): config.h: stamp-h1 @test -f $@ || rm -f stamp-h1 @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status @rm -f stamp-h1 cd $(top_builddir) && $(SHELL) ./config.status config.h $(srcdir)/config.h.in: $(am__configure_deps) ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) rm -f stamp-h1 touch $@ distclean-hdr: -rm -f config.h stamp-h1 doc/Doxyfile: $(top_builddir)/config.status $(top_srcdir)/doc/Doxyfile.in cd $(top_builddir) && $(SHELL) ./config.status $@ wimlib.pc: $(top_builddir)/config.status $(srcdir)/wimlib.pc.in cd $(top_builddir) && $(SHELL) ./config.status $@ programs/mkwinpeimg: $(top_builddir)/config.status $(top_srcdir)/programs/mkwinpeimg.in cd $(top_builddir) && $(SHELL) ./config.status $@ install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list clean-checkPROGRAMS: @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } src/$(am__dirstamp): @$(MKDIR_P) src @: > src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) src/$(DEPDIR) @: > src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-add_image.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-avl_tree.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-blob_table.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-compress.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-compress_common.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-compress_parallel.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-compress_serial.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-decompress.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-decompress_common.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-delete_image.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-dentry.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-divsufsort.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-encoding.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-error.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-export_image.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-extract.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-file_io.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-header.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-inode.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-inode_fixup.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-inode_table.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-integrity.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-iterate_dir.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-join.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-lcpit_matchfinder.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-lzms_common.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-lzms_compress.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-lzms_decompress.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-lzx_common.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-lzx_compress.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-lzx_decompress.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-metadata_resource.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-mount_image.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-pathlist.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-paths.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-pattern.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-progress.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-reference.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-registry.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-reparse.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-resource.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-scan.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-security.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-sha1.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-solid.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-split.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-tagged_items.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-template.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-textfile.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-timestamp.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-update_image.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-util.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-verify.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-wim.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-write.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-x86_cpu_features.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-xml.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-xml_windows.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-xpress_compress.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-xpress_decompress.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-ntfs-3g_apply.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-ntfs-3g_capture.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-wimboot.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-win32_common.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-win32_apply.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-win32_capture.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-win32_replacements.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-win32_vss.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-unix_apply.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-unix_capture.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libwim_la-test_support.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) libwim.la: $(libwim_la_OBJECTS) $(libwim_la_DEPENDENCIES) $(EXTRA_libwim_la_DEPENDENCIES) $(AM_V_CCLD)$(libwim_la_LINK) -rpath $(libdir) $(libwim_la_OBJECTS) $(libwim_la_LIBADD) $(LIBS) tests/$(am__dirstamp): @$(MKDIR_P) tests @: > tests/$(am__dirstamp) tests/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) tests/$(DEPDIR) @: > tests/$(DEPDIR)/$(am__dirstamp) tests/tree-cmp.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/tree-cmp$(EXEEXT): $(tests_tree_cmp_OBJECTS) $(tests_tree_cmp_DEPENDENCIES) $(EXTRA_tests_tree_cmp_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/tree-cmp$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_tree_cmp_OBJECTS) $(tests_tree_cmp_LDADD) $(LIBS) tests/wlfuzz.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/wlfuzz$(EXEEXT): $(tests_wlfuzz_OBJECTS) $(tests_wlfuzz_DEPENDENCIES) $(EXTRA_tests_wlfuzz_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/wlfuzz$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_wlfuzz_OBJECTS) $(tests_wlfuzz_LDADD) $(LIBS) programs/$(am__dirstamp): @$(MKDIR_P) programs @: > programs/$(am__dirstamp) programs/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) programs/$(DEPDIR) @: > programs/$(DEPDIR)/$(am__dirstamp) programs/wimlib_imagex-imagex.$(OBJEXT): programs/$(am__dirstamp) \ programs/$(DEPDIR)/$(am__dirstamp) programs/wimlib_imagex-imagex-win32.$(OBJEXT): \ programs/$(am__dirstamp) programs/$(DEPDIR)/$(am__dirstamp) programs/wimlib_imagex-wgetopt.$(OBJEXT): programs/$(am__dirstamp) \ programs/$(DEPDIR)/$(am__dirstamp) wimlib-imagex$(EXEEXT): $(wimlib_imagex_OBJECTS) $(wimlib_imagex_DEPENDENCIES) $(EXTRA_wimlib_imagex_DEPENDENCIES) @rm -f wimlib-imagex$(EXEEXT) $(AM_V_CCLD)$(wimlib_imagex_LINK) $(wimlib_imagex_OBJECTS) $(wimlib_imagex_LDADD) $(LIBS) install-dist_binSCRIPTS: $(dist_bin_SCRIPTS) @$(NORMAL_INSTALL) @list='$(dist_bin_SCRIPTS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-dist_binSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(dist_bin_SCRIPTS)'; test -n "$(bindir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ dir='$(DESTDIR)$(bindir)'; $(am__uninstall_files_from_dir) mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f programs/*.$(OBJEXT) -rm -f src/*.$(OBJEXT) -rm -f src/*.lo -rm -f tests/*.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@programs/$(DEPDIR)/wimlib_imagex-imagex-win32.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@programs/$(DEPDIR)/wimlib_imagex-imagex.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@programs/$(DEPDIR)/wimlib_imagex-wgetopt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-add_image.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-avl_tree.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-blob_table.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-compress.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-compress_common.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-compress_parallel.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-compress_serial.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-decompress.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-decompress_common.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-delete_image.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-dentry.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-divsufsort.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-encoding.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-error.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-export_image.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-extract.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-file_io.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-header.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-inode.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-inode_fixup.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-inode_table.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-integrity.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-iterate_dir.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-join.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-lcpit_matchfinder.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-lzms_common.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-lzms_compress.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-lzms_decompress.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-lzx_common.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-lzx_compress.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-lzx_decompress.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-metadata_resource.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-mount_image.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-ntfs-3g_apply.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-ntfs-3g_capture.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-pathlist.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-paths.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-pattern.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-progress.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-reference.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-registry.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-reparse.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-resource.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-scan.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-security.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-sha1.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-solid.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-split.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-tagged_items.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-template.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-test_support.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-textfile.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-timestamp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-unix_apply.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-unix_capture.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-update_image.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-util.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-verify.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-wim.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-wimboot.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-win32_apply.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-win32_capture.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-win32_common.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-win32_replacements.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-win32_vss.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-write.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-x86_cpu_features.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-xml.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-xml_windows.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-xpress_compress.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libwim_la-xpress_decompress.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/tree-cmp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/wlfuzz.Po@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @echo '# dummy' >$@-t && $(am__mv) $@-t $@ am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< src/libwim_la-add_image.lo: src/add_image.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-add_image.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-add_image.Tpo -c -o src/libwim_la-add_image.lo `test -f 'src/add_image.c' || echo '$(srcdir)/'`src/add_image.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-add_image.Tpo src/$(DEPDIR)/libwim_la-add_image.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/add_image.c' object='src/libwim_la-add_image.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-add_image.lo `test -f 'src/add_image.c' || echo '$(srcdir)/'`src/add_image.c src/libwim_la-avl_tree.lo: src/avl_tree.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-avl_tree.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-avl_tree.Tpo -c -o src/libwim_la-avl_tree.lo `test -f 'src/avl_tree.c' || echo '$(srcdir)/'`src/avl_tree.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-avl_tree.Tpo src/$(DEPDIR)/libwim_la-avl_tree.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/avl_tree.c' object='src/libwim_la-avl_tree.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-avl_tree.lo `test -f 'src/avl_tree.c' || echo '$(srcdir)/'`src/avl_tree.c src/libwim_la-blob_table.lo: src/blob_table.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-blob_table.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-blob_table.Tpo -c -o src/libwim_la-blob_table.lo `test -f 'src/blob_table.c' || echo '$(srcdir)/'`src/blob_table.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-blob_table.Tpo src/$(DEPDIR)/libwim_la-blob_table.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/blob_table.c' object='src/libwim_la-blob_table.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-blob_table.lo `test -f 'src/blob_table.c' || echo '$(srcdir)/'`src/blob_table.c src/libwim_la-compress.lo: src/compress.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-compress.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-compress.Tpo -c -o src/libwim_la-compress.lo `test -f 'src/compress.c' || echo '$(srcdir)/'`src/compress.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-compress.Tpo src/$(DEPDIR)/libwim_la-compress.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/compress.c' object='src/libwim_la-compress.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-compress.lo `test -f 'src/compress.c' || echo '$(srcdir)/'`src/compress.c src/libwim_la-compress_common.lo: src/compress_common.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-compress_common.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-compress_common.Tpo -c -o src/libwim_la-compress_common.lo `test -f 'src/compress_common.c' || echo '$(srcdir)/'`src/compress_common.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-compress_common.Tpo src/$(DEPDIR)/libwim_la-compress_common.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/compress_common.c' object='src/libwim_la-compress_common.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-compress_common.lo `test -f 'src/compress_common.c' || echo '$(srcdir)/'`src/compress_common.c src/libwim_la-compress_parallel.lo: src/compress_parallel.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-compress_parallel.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-compress_parallel.Tpo -c -o src/libwim_la-compress_parallel.lo `test -f 'src/compress_parallel.c' || echo '$(srcdir)/'`src/compress_parallel.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-compress_parallel.Tpo src/$(DEPDIR)/libwim_la-compress_parallel.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/compress_parallel.c' object='src/libwim_la-compress_parallel.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-compress_parallel.lo `test -f 'src/compress_parallel.c' || echo '$(srcdir)/'`src/compress_parallel.c src/libwim_la-compress_serial.lo: src/compress_serial.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-compress_serial.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-compress_serial.Tpo -c -o src/libwim_la-compress_serial.lo `test -f 'src/compress_serial.c' || echo '$(srcdir)/'`src/compress_serial.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-compress_serial.Tpo src/$(DEPDIR)/libwim_la-compress_serial.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/compress_serial.c' object='src/libwim_la-compress_serial.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-compress_serial.lo `test -f 'src/compress_serial.c' || echo '$(srcdir)/'`src/compress_serial.c src/libwim_la-decompress.lo: src/decompress.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-decompress.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-decompress.Tpo -c -o src/libwim_la-decompress.lo `test -f 'src/decompress.c' || echo '$(srcdir)/'`src/decompress.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-decompress.Tpo src/$(DEPDIR)/libwim_la-decompress.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/decompress.c' object='src/libwim_la-decompress.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-decompress.lo `test -f 'src/decompress.c' || echo '$(srcdir)/'`src/decompress.c src/libwim_la-decompress_common.lo: src/decompress_common.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-decompress_common.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-decompress_common.Tpo -c -o src/libwim_la-decompress_common.lo `test -f 'src/decompress_common.c' || echo '$(srcdir)/'`src/decompress_common.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-decompress_common.Tpo src/$(DEPDIR)/libwim_la-decompress_common.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/decompress_common.c' object='src/libwim_la-decompress_common.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-decompress_common.lo `test -f 'src/decompress_common.c' || echo '$(srcdir)/'`src/decompress_common.c src/libwim_la-delete_image.lo: src/delete_image.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-delete_image.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-delete_image.Tpo -c -o src/libwim_la-delete_image.lo `test -f 'src/delete_image.c' || echo '$(srcdir)/'`src/delete_image.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-delete_image.Tpo src/$(DEPDIR)/libwim_la-delete_image.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/delete_image.c' object='src/libwim_la-delete_image.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-delete_image.lo `test -f 'src/delete_image.c' || echo '$(srcdir)/'`src/delete_image.c src/libwim_la-dentry.lo: src/dentry.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-dentry.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-dentry.Tpo -c -o src/libwim_la-dentry.lo `test -f 'src/dentry.c' || echo '$(srcdir)/'`src/dentry.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-dentry.Tpo src/$(DEPDIR)/libwim_la-dentry.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/dentry.c' object='src/libwim_la-dentry.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-dentry.lo `test -f 'src/dentry.c' || echo '$(srcdir)/'`src/dentry.c src/libwim_la-divsufsort.lo: src/divsufsort.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-divsufsort.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-divsufsort.Tpo -c -o src/libwim_la-divsufsort.lo `test -f 'src/divsufsort.c' || echo '$(srcdir)/'`src/divsufsort.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-divsufsort.Tpo src/$(DEPDIR)/libwim_la-divsufsort.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/divsufsort.c' object='src/libwim_la-divsufsort.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-divsufsort.lo `test -f 'src/divsufsort.c' || echo '$(srcdir)/'`src/divsufsort.c src/libwim_la-encoding.lo: src/encoding.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-encoding.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-encoding.Tpo -c -o src/libwim_la-encoding.lo `test -f 'src/encoding.c' || echo '$(srcdir)/'`src/encoding.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-encoding.Tpo src/$(DEPDIR)/libwim_la-encoding.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/encoding.c' object='src/libwim_la-encoding.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-encoding.lo `test -f 'src/encoding.c' || echo '$(srcdir)/'`src/encoding.c src/libwim_la-error.lo: src/error.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-error.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-error.Tpo -c -o src/libwim_la-error.lo `test -f 'src/error.c' || echo '$(srcdir)/'`src/error.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-error.Tpo src/$(DEPDIR)/libwim_la-error.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/error.c' object='src/libwim_la-error.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-error.lo `test -f 'src/error.c' || echo '$(srcdir)/'`src/error.c src/libwim_la-export_image.lo: src/export_image.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-export_image.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-export_image.Tpo -c -o src/libwim_la-export_image.lo `test -f 'src/export_image.c' || echo '$(srcdir)/'`src/export_image.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-export_image.Tpo src/$(DEPDIR)/libwim_la-export_image.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/export_image.c' object='src/libwim_la-export_image.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-export_image.lo `test -f 'src/export_image.c' || echo '$(srcdir)/'`src/export_image.c src/libwim_la-extract.lo: src/extract.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-extract.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-extract.Tpo -c -o src/libwim_la-extract.lo `test -f 'src/extract.c' || echo '$(srcdir)/'`src/extract.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-extract.Tpo src/$(DEPDIR)/libwim_la-extract.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/extract.c' object='src/libwim_la-extract.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-extract.lo `test -f 'src/extract.c' || echo '$(srcdir)/'`src/extract.c src/libwim_la-file_io.lo: src/file_io.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-file_io.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-file_io.Tpo -c -o src/libwim_la-file_io.lo `test -f 'src/file_io.c' || echo '$(srcdir)/'`src/file_io.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-file_io.Tpo src/$(DEPDIR)/libwim_la-file_io.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/file_io.c' object='src/libwim_la-file_io.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-file_io.lo `test -f 'src/file_io.c' || echo '$(srcdir)/'`src/file_io.c src/libwim_la-header.lo: src/header.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-header.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-header.Tpo -c -o src/libwim_la-header.lo `test -f 'src/header.c' || echo '$(srcdir)/'`src/header.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-header.Tpo src/$(DEPDIR)/libwim_la-header.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/header.c' object='src/libwim_la-header.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-header.lo `test -f 'src/header.c' || echo '$(srcdir)/'`src/header.c src/libwim_la-inode.lo: src/inode.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-inode.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-inode.Tpo -c -o src/libwim_la-inode.lo `test -f 'src/inode.c' || echo '$(srcdir)/'`src/inode.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-inode.Tpo src/$(DEPDIR)/libwim_la-inode.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/inode.c' object='src/libwim_la-inode.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-inode.lo `test -f 'src/inode.c' || echo '$(srcdir)/'`src/inode.c src/libwim_la-inode_fixup.lo: src/inode_fixup.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-inode_fixup.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-inode_fixup.Tpo -c -o src/libwim_la-inode_fixup.lo `test -f 'src/inode_fixup.c' || echo '$(srcdir)/'`src/inode_fixup.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-inode_fixup.Tpo src/$(DEPDIR)/libwim_la-inode_fixup.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/inode_fixup.c' object='src/libwim_la-inode_fixup.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-inode_fixup.lo `test -f 'src/inode_fixup.c' || echo '$(srcdir)/'`src/inode_fixup.c src/libwim_la-inode_table.lo: src/inode_table.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-inode_table.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-inode_table.Tpo -c -o src/libwim_la-inode_table.lo `test -f 'src/inode_table.c' || echo '$(srcdir)/'`src/inode_table.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-inode_table.Tpo src/$(DEPDIR)/libwim_la-inode_table.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/inode_table.c' object='src/libwim_la-inode_table.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-inode_table.lo `test -f 'src/inode_table.c' || echo '$(srcdir)/'`src/inode_table.c src/libwim_la-integrity.lo: src/integrity.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-integrity.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-integrity.Tpo -c -o src/libwim_la-integrity.lo `test -f 'src/integrity.c' || echo '$(srcdir)/'`src/integrity.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-integrity.Tpo src/$(DEPDIR)/libwim_la-integrity.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/integrity.c' object='src/libwim_la-integrity.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-integrity.lo `test -f 'src/integrity.c' || echo '$(srcdir)/'`src/integrity.c src/libwim_la-iterate_dir.lo: src/iterate_dir.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-iterate_dir.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-iterate_dir.Tpo -c -o src/libwim_la-iterate_dir.lo `test -f 'src/iterate_dir.c' || echo '$(srcdir)/'`src/iterate_dir.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-iterate_dir.Tpo src/$(DEPDIR)/libwim_la-iterate_dir.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/iterate_dir.c' object='src/libwim_la-iterate_dir.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-iterate_dir.lo `test -f 'src/iterate_dir.c' || echo '$(srcdir)/'`src/iterate_dir.c src/libwim_la-join.lo: src/join.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-join.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-join.Tpo -c -o src/libwim_la-join.lo `test -f 'src/join.c' || echo '$(srcdir)/'`src/join.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-join.Tpo src/$(DEPDIR)/libwim_la-join.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/join.c' object='src/libwim_la-join.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-join.lo `test -f 'src/join.c' || echo '$(srcdir)/'`src/join.c src/libwim_la-lcpit_matchfinder.lo: src/lcpit_matchfinder.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-lcpit_matchfinder.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-lcpit_matchfinder.Tpo -c -o src/libwim_la-lcpit_matchfinder.lo `test -f 'src/lcpit_matchfinder.c' || echo '$(srcdir)/'`src/lcpit_matchfinder.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-lcpit_matchfinder.Tpo src/$(DEPDIR)/libwim_la-lcpit_matchfinder.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/lcpit_matchfinder.c' object='src/libwim_la-lcpit_matchfinder.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-lcpit_matchfinder.lo `test -f 'src/lcpit_matchfinder.c' || echo '$(srcdir)/'`src/lcpit_matchfinder.c src/libwim_la-lzms_common.lo: src/lzms_common.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-lzms_common.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-lzms_common.Tpo -c -o src/libwim_la-lzms_common.lo `test -f 'src/lzms_common.c' || echo '$(srcdir)/'`src/lzms_common.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-lzms_common.Tpo src/$(DEPDIR)/libwim_la-lzms_common.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/lzms_common.c' object='src/libwim_la-lzms_common.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-lzms_common.lo `test -f 'src/lzms_common.c' || echo '$(srcdir)/'`src/lzms_common.c src/libwim_la-lzms_compress.lo: src/lzms_compress.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-lzms_compress.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-lzms_compress.Tpo -c -o src/libwim_la-lzms_compress.lo `test -f 'src/lzms_compress.c' || echo '$(srcdir)/'`src/lzms_compress.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-lzms_compress.Tpo src/$(DEPDIR)/libwim_la-lzms_compress.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/lzms_compress.c' object='src/libwim_la-lzms_compress.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-lzms_compress.lo `test -f 'src/lzms_compress.c' || echo '$(srcdir)/'`src/lzms_compress.c src/libwim_la-lzms_decompress.lo: src/lzms_decompress.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-lzms_decompress.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-lzms_decompress.Tpo -c -o src/libwim_la-lzms_decompress.lo `test -f 'src/lzms_decompress.c' || echo '$(srcdir)/'`src/lzms_decompress.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-lzms_decompress.Tpo src/$(DEPDIR)/libwim_la-lzms_decompress.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/lzms_decompress.c' object='src/libwim_la-lzms_decompress.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-lzms_decompress.lo `test -f 'src/lzms_decompress.c' || echo '$(srcdir)/'`src/lzms_decompress.c src/libwim_la-lzx_common.lo: src/lzx_common.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-lzx_common.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-lzx_common.Tpo -c -o src/libwim_la-lzx_common.lo `test -f 'src/lzx_common.c' || echo '$(srcdir)/'`src/lzx_common.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-lzx_common.Tpo src/$(DEPDIR)/libwim_la-lzx_common.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/lzx_common.c' object='src/libwim_la-lzx_common.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-lzx_common.lo `test -f 'src/lzx_common.c' || echo '$(srcdir)/'`src/lzx_common.c src/libwim_la-lzx_compress.lo: src/lzx_compress.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-lzx_compress.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-lzx_compress.Tpo -c -o src/libwim_la-lzx_compress.lo `test -f 'src/lzx_compress.c' || echo '$(srcdir)/'`src/lzx_compress.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-lzx_compress.Tpo src/$(DEPDIR)/libwim_la-lzx_compress.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/lzx_compress.c' object='src/libwim_la-lzx_compress.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-lzx_compress.lo `test -f 'src/lzx_compress.c' || echo '$(srcdir)/'`src/lzx_compress.c src/libwim_la-lzx_decompress.lo: src/lzx_decompress.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-lzx_decompress.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-lzx_decompress.Tpo -c -o src/libwim_la-lzx_decompress.lo `test -f 'src/lzx_decompress.c' || echo '$(srcdir)/'`src/lzx_decompress.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-lzx_decompress.Tpo src/$(DEPDIR)/libwim_la-lzx_decompress.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/lzx_decompress.c' object='src/libwim_la-lzx_decompress.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-lzx_decompress.lo `test -f 'src/lzx_decompress.c' || echo '$(srcdir)/'`src/lzx_decompress.c src/libwim_la-metadata_resource.lo: src/metadata_resource.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-metadata_resource.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-metadata_resource.Tpo -c -o src/libwim_la-metadata_resource.lo `test -f 'src/metadata_resource.c' || echo '$(srcdir)/'`src/metadata_resource.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-metadata_resource.Tpo src/$(DEPDIR)/libwim_la-metadata_resource.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/metadata_resource.c' object='src/libwim_la-metadata_resource.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-metadata_resource.lo `test -f 'src/metadata_resource.c' || echo '$(srcdir)/'`src/metadata_resource.c src/libwim_la-mount_image.lo: src/mount_image.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-mount_image.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-mount_image.Tpo -c -o src/libwim_la-mount_image.lo `test -f 'src/mount_image.c' || echo '$(srcdir)/'`src/mount_image.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-mount_image.Tpo src/$(DEPDIR)/libwim_la-mount_image.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/mount_image.c' object='src/libwim_la-mount_image.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-mount_image.lo `test -f 'src/mount_image.c' || echo '$(srcdir)/'`src/mount_image.c src/libwim_la-pathlist.lo: src/pathlist.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-pathlist.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-pathlist.Tpo -c -o src/libwim_la-pathlist.lo `test -f 'src/pathlist.c' || echo '$(srcdir)/'`src/pathlist.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-pathlist.Tpo src/$(DEPDIR)/libwim_la-pathlist.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/pathlist.c' object='src/libwim_la-pathlist.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-pathlist.lo `test -f 'src/pathlist.c' || echo '$(srcdir)/'`src/pathlist.c src/libwim_la-paths.lo: src/paths.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-paths.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-paths.Tpo -c -o src/libwim_la-paths.lo `test -f 'src/paths.c' || echo '$(srcdir)/'`src/paths.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-paths.Tpo src/$(DEPDIR)/libwim_la-paths.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/paths.c' object='src/libwim_la-paths.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-paths.lo `test -f 'src/paths.c' || echo '$(srcdir)/'`src/paths.c src/libwim_la-pattern.lo: src/pattern.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-pattern.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-pattern.Tpo -c -o src/libwim_la-pattern.lo `test -f 'src/pattern.c' || echo '$(srcdir)/'`src/pattern.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-pattern.Tpo src/$(DEPDIR)/libwim_la-pattern.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/pattern.c' object='src/libwim_la-pattern.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-pattern.lo `test -f 'src/pattern.c' || echo '$(srcdir)/'`src/pattern.c src/libwim_la-progress.lo: src/progress.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-progress.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-progress.Tpo -c -o src/libwim_la-progress.lo `test -f 'src/progress.c' || echo '$(srcdir)/'`src/progress.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-progress.Tpo src/$(DEPDIR)/libwim_la-progress.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/progress.c' object='src/libwim_la-progress.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-progress.lo `test -f 'src/progress.c' || echo '$(srcdir)/'`src/progress.c src/libwim_la-reference.lo: src/reference.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-reference.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-reference.Tpo -c -o src/libwim_la-reference.lo `test -f 'src/reference.c' || echo '$(srcdir)/'`src/reference.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-reference.Tpo src/$(DEPDIR)/libwim_la-reference.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/reference.c' object='src/libwim_la-reference.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-reference.lo `test -f 'src/reference.c' || echo '$(srcdir)/'`src/reference.c src/libwim_la-registry.lo: src/registry.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-registry.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-registry.Tpo -c -o src/libwim_la-registry.lo `test -f 'src/registry.c' || echo '$(srcdir)/'`src/registry.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-registry.Tpo src/$(DEPDIR)/libwim_la-registry.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/registry.c' object='src/libwim_la-registry.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-registry.lo `test -f 'src/registry.c' || echo '$(srcdir)/'`src/registry.c src/libwim_la-reparse.lo: src/reparse.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-reparse.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-reparse.Tpo -c -o src/libwim_la-reparse.lo `test -f 'src/reparse.c' || echo '$(srcdir)/'`src/reparse.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-reparse.Tpo src/$(DEPDIR)/libwim_la-reparse.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/reparse.c' object='src/libwim_la-reparse.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-reparse.lo `test -f 'src/reparse.c' || echo '$(srcdir)/'`src/reparse.c src/libwim_la-resource.lo: src/resource.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-resource.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-resource.Tpo -c -o src/libwim_la-resource.lo `test -f 'src/resource.c' || echo '$(srcdir)/'`src/resource.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-resource.Tpo src/$(DEPDIR)/libwim_la-resource.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/resource.c' object='src/libwim_la-resource.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-resource.lo `test -f 'src/resource.c' || echo '$(srcdir)/'`src/resource.c src/libwim_la-scan.lo: src/scan.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-scan.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-scan.Tpo -c -o src/libwim_la-scan.lo `test -f 'src/scan.c' || echo '$(srcdir)/'`src/scan.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-scan.Tpo src/$(DEPDIR)/libwim_la-scan.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/scan.c' object='src/libwim_la-scan.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-scan.lo `test -f 'src/scan.c' || echo '$(srcdir)/'`src/scan.c src/libwim_la-security.lo: src/security.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-security.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-security.Tpo -c -o src/libwim_la-security.lo `test -f 'src/security.c' || echo '$(srcdir)/'`src/security.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-security.Tpo src/$(DEPDIR)/libwim_la-security.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/security.c' object='src/libwim_la-security.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-security.lo `test -f 'src/security.c' || echo '$(srcdir)/'`src/security.c src/libwim_la-sha1.lo: src/sha1.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-sha1.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-sha1.Tpo -c -o src/libwim_la-sha1.lo `test -f 'src/sha1.c' || echo '$(srcdir)/'`src/sha1.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-sha1.Tpo src/$(DEPDIR)/libwim_la-sha1.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/sha1.c' object='src/libwim_la-sha1.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-sha1.lo `test -f 'src/sha1.c' || echo '$(srcdir)/'`src/sha1.c src/libwim_la-solid.lo: src/solid.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-solid.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-solid.Tpo -c -o src/libwim_la-solid.lo `test -f 'src/solid.c' || echo '$(srcdir)/'`src/solid.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-solid.Tpo src/$(DEPDIR)/libwim_la-solid.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/solid.c' object='src/libwim_la-solid.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-solid.lo `test -f 'src/solid.c' || echo '$(srcdir)/'`src/solid.c src/libwim_la-split.lo: src/split.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-split.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-split.Tpo -c -o src/libwim_la-split.lo `test -f 'src/split.c' || echo '$(srcdir)/'`src/split.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-split.Tpo src/$(DEPDIR)/libwim_la-split.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/split.c' object='src/libwim_la-split.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-split.lo `test -f 'src/split.c' || echo '$(srcdir)/'`src/split.c src/libwim_la-tagged_items.lo: src/tagged_items.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-tagged_items.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-tagged_items.Tpo -c -o src/libwim_la-tagged_items.lo `test -f 'src/tagged_items.c' || echo '$(srcdir)/'`src/tagged_items.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-tagged_items.Tpo src/$(DEPDIR)/libwim_la-tagged_items.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/tagged_items.c' object='src/libwim_la-tagged_items.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-tagged_items.lo `test -f 'src/tagged_items.c' || echo '$(srcdir)/'`src/tagged_items.c src/libwim_la-template.lo: src/template.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-template.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-template.Tpo -c -o src/libwim_la-template.lo `test -f 'src/template.c' || echo '$(srcdir)/'`src/template.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-template.Tpo src/$(DEPDIR)/libwim_la-template.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/template.c' object='src/libwim_la-template.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-template.lo `test -f 'src/template.c' || echo '$(srcdir)/'`src/template.c src/libwim_la-textfile.lo: src/textfile.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-textfile.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-textfile.Tpo -c -o src/libwim_la-textfile.lo `test -f 'src/textfile.c' || echo '$(srcdir)/'`src/textfile.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-textfile.Tpo src/$(DEPDIR)/libwim_la-textfile.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/textfile.c' object='src/libwim_la-textfile.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-textfile.lo `test -f 'src/textfile.c' || echo '$(srcdir)/'`src/textfile.c src/libwim_la-timestamp.lo: src/timestamp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-timestamp.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-timestamp.Tpo -c -o src/libwim_la-timestamp.lo `test -f 'src/timestamp.c' || echo '$(srcdir)/'`src/timestamp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-timestamp.Tpo src/$(DEPDIR)/libwim_la-timestamp.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/timestamp.c' object='src/libwim_la-timestamp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-timestamp.lo `test -f 'src/timestamp.c' || echo '$(srcdir)/'`src/timestamp.c src/libwim_la-update_image.lo: src/update_image.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-update_image.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-update_image.Tpo -c -o src/libwim_la-update_image.lo `test -f 'src/update_image.c' || echo '$(srcdir)/'`src/update_image.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-update_image.Tpo src/$(DEPDIR)/libwim_la-update_image.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/update_image.c' object='src/libwim_la-update_image.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-update_image.lo `test -f 'src/update_image.c' || echo '$(srcdir)/'`src/update_image.c src/libwim_la-util.lo: src/util.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-util.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-util.Tpo -c -o src/libwim_la-util.lo `test -f 'src/util.c' || echo '$(srcdir)/'`src/util.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-util.Tpo src/$(DEPDIR)/libwim_la-util.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/util.c' object='src/libwim_la-util.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-util.lo `test -f 'src/util.c' || echo '$(srcdir)/'`src/util.c src/libwim_la-verify.lo: src/verify.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-verify.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-verify.Tpo -c -o src/libwim_la-verify.lo `test -f 'src/verify.c' || echo '$(srcdir)/'`src/verify.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-verify.Tpo src/$(DEPDIR)/libwim_la-verify.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/verify.c' object='src/libwim_la-verify.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-verify.lo `test -f 'src/verify.c' || echo '$(srcdir)/'`src/verify.c src/libwim_la-wim.lo: src/wim.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-wim.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-wim.Tpo -c -o src/libwim_la-wim.lo `test -f 'src/wim.c' || echo '$(srcdir)/'`src/wim.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-wim.Tpo src/$(DEPDIR)/libwim_la-wim.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/wim.c' object='src/libwim_la-wim.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-wim.lo `test -f 'src/wim.c' || echo '$(srcdir)/'`src/wim.c src/libwim_la-write.lo: src/write.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-write.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-write.Tpo -c -o src/libwim_la-write.lo `test -f 'src/write.c' || echo '$(srcdir)/'`src/write.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-write.Tpo src/$(DEPDIR)/libwim_la-write.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/write.c' object='src/libwim_la-write.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-write.lo `test -f 'src/write.c' || echo '$(srcdir)/'`src/write.c src/libwim_la-x86_cpu_features.lo: src/x86_cpu_features.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-x86_cpu_features.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-x86_cpu_features.Tpo -c -o src/libwim_la-x86_cpu_features.lo `test -f 'src/x86_cpu_features.c' || echo '$(srcdir)/'`src/x86_cpu_features.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-x86_cpu_features.Tpo src/$(DEPDIR)/libwim_la-x86_cpu_features.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/x86_cpu_features.c' object='src/libwim_la-x86_cpu_features.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-x86_cpu_features.lo `test -f 'src/x86_cpu_features.c' || echo '$(srcdir)/'`src/x86_cpu_features.c src/libwim_la-xml.lo: src/xml.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-xml.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-xml.Tpo -c -o src/libwim_la-xml.lo `test -f 'src/xml.c' || echo '$(srcdir)/'`src/xml.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-xml.Tpo src/$(DEPDIR)/libwim_la-xml.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/xml.c' object='src/libwim_la-xml.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-xml.lo `test -f 'src/xml.c' || echo '$(srcdir)/'`src/xml.c src/libwim_la-xml_windows.lo: src/xml_windows.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-xml_windows.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-xml_windows.Tpo -c -o src/libwim_la-xml_windows.lo `test -f 'src/xml_windows.c' || echo '$(srcdir)/'`src/xml_windows.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-xml_windows.Tpo src/$(DEPDIR)/libwim_la-xml_windows.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/xml_windows.c' object='src/libwim_la-xml_windows.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-xml_windows.lo `test -f 'src/xml_windows.c' || echo '$(srcdir)/'`src/xml_windows.c src/libwim_la-xpress_compress.lo: src/xpress_compress.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-xpress_compress.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-xpress_compress.Tpo -c -o src/libwim_la-xpress_compress.lo `test -f 'src/xpress_compress.c' || echo '$(srcdir)/'`src/xpress_compress.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-xpress_compress.Tpo src/$(DEPDIR)/libwim_la-xpress_compress.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/xpress_compress.c' object='src/libwim_la-xpress_compress.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-xpress_compress.lo `test -f 'src/xpress_compress.c' || echo '$(srcdir)/'`src/xpress_compress.c src/libwim_la-xpress_decompress.lo: src/xpress_decompress.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-xpress_decompress.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-xpress_decompress.Tpo -c -o src/libwim_la-xpress_decompress.lo `test -f 'src/xpress_decompress.c' || echo '$(srcdir)/'`src/xpress_decompress.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-xpress_decompress.Tpo src/$(DEPDIR)/libwim_la-xpress_decompress.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/xpress_decompress.c' object='src/libwim_la-xpress_decompress.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-xpress_decompress.lo `test -f 'src/xpress_decompress.c' || echo '$(srcdir)/'`src/xpress_decompress.c src/libwim_la-ntfs-3g_apply.lo: src/ntfs-3g_apply.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-ntfs-3g_apply.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-ntfs-3g_apply.Tpo -c -o src/libwim_la-ntfs-3g_apply.lo `test -f 'src/ntfs-3g_apply.c' || echo '$(srcdir)/'`src/ntfs-3g_apply.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-ntfs-3g_apply.Tpo src/$(DEPDIR)/libwim_la-ntfs-3g_apply.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/ntfs-3g_apply.c' object='src/libwim_la-ntfs-3g_apply.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-ntfs-3g_apply.lo `test -f 'src/ntfs-3g_apply.c' || echo '$(srcdir)/'`src/ntfs-3g_apply.c src/libwim_la-ntfs-3g_capture.lo: src/ntfs-3g_capture.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-ntfs-3g_capture.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-ntfs-3g_capture.Tpo -c -o src/libwim_la-ntfs-3g_capture.lo `test -f 'src/ntfs-3g_capture.c' || echo '$(srcdir)/'`src/ntfs-3g_capture.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-ntfs-3g_capture.Tpo src/$(DEPDIR)/libwim_la-ntfs-3g_capture.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/ntfs-3g_capture.c' object='src/libwim_la-ntfs-3g_capture.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-ntfs-3g_capture.lo `test -f 'src/ntfs-3g_capture.c' || echo '$(srcdir)/'`src/ntfs-3g_capture.c src/libwim_la-wimboot.lo: src/wimboot.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-wimboot.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-wimboot.Tpo -c -o src/libwim_la-wimboot.lo `test -f 'src/wimboot.c' || echo '$(srcdir)/'`src/wimboot.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-wimboot.Tpo src/$(DEPDIR)/libwim_la-wimboot.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/wimboot.c' object='src/libwim_la-wimboot.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-wimboot.lo `test -f 'src/wimboot.c' || echo '$(srcdir)/'`src/wimboot.c src/libwim_la-win32_common.lo: src/win32_common.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-win32_common.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-win32_common.Tpo -c -o src/libwim_la-win32_common.lo `test -f 'src/win32_common.c' || echo '$(srcdir)/'`src/win32_common.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-win32_common.Tpo src/$(DEPDIR)/libwim_la-win32_common.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/win32_common.c' object='src/libwim_la-win32_common.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-win32_common.lo `test -f 'src/win32_common.c' || echo '$(srcdir)/'`src/win32_common.c src/libwim_la-win32_apply.lo: src/win32_apply.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-win32_apply.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-win32_apply.Tpo -c -o src/libwim_la-win32_apply.lo `test -f 'src/win32_apply.c' || echo '$(srcdir)/'`src/win32_apply.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-win32_apply.Tpo src/$(DEPDIR)/libwim_la-win32_apply.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/win32_apply.c' object='src/libwim_la-win32_apply.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-win32_apply.lo `test -f 'src/win32_apply.c' || echo '$(srcdir)/'`src/win32_apply.c src/libwim_la-win32_capture.lo: src/win32_capture.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-win32_capture.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-win32_capture.Tpo -c -o src/libwim_la-win32_capture.lo `test -f 'src/win32_capture.c' || echo '$(srcdir)/'`src/win32_capture.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-win32_capture.Tpo src/$(DEPDIR)/libwim_la-win32_capture.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/win32_capture.c' object='src/libwim_la-win32_capture.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-win32_capture.lo `test -f 'src/win32_capture.c' || echo '$(srcdir)/'`src/win32_capture.c src/libwim_la-win32_replacements.lo: src/win32_replacements.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-win32_replacements.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-win32_replacements.Tpo -c -o src/libwim_la-win32_replacements.lo `test -f 'src/win32_replacements.c' || echo '$(srcdir)/'`src/win32_replacements.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-win32_replacements.Tpo src/$(DEPDIR)/libwim_la-win32_replacements.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/win32_replacements.c' object='src/libwim_la-win32_replacements.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-win32_replacements.lo `test -f 'src/win32_replacements.c' || echo '$(srcdir)/'`src/win32_replacements.c src/libwim_la-win32_vss.lo: src/win32_vss.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-win32_vss.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-win32_vss.Tpo -c -o src/libwim_la-win32_vss.lo `test -f 'src/win32_vss.c' || echo '$(srcdir)/'`src/win32_vss.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-win32_vss.Tpo src/$(DEPDIR)/libwim_la-win32_vss.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/win32_vss.c' object='src/libwim_la-win32_vss.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-win32_vss.lo `test -f 'src/win32_vss.c' || echo '$(srcdir)/'`src/win32_vss.c src/libwim_la-unix_apply.lo: src/unix_apply.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-unix_apply.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-unix_apply.Tpo -c -o src/libwim_la-unix_apply.lo `test -f 'src/unix_apply.c' || echo '$(srcdir)/'`src/unix_apply.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-unix_apply.Tpo src/$(DEPDIR)/libwim_la-unix_apply.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/unix_apply.c' object='src/libwim_la-unix_apply.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-unix_apply.lo `test -f 'src/unix_apply.c' || echo '$(srcdir)/'`src/unix_apply.c src/libwim_la-unix_capture.lo: src/unix_capture.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-unix_capture.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-unix_capture.Tpo -c -o src/libwim_la-unix_capture.lo `test -f 'src/unix_capture.c' || echo '$(srcdir)/'`src/unix_capture.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-unix_capture.Tpo src/$(DEPDIR)/libwim_la-unix_capture.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/unix_capture.c' object='src/libwim_la-unix_capture.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-unix_capture.lo `test -f 'src/unix_capture.c' || echo '$(srcdir)/'`src/unix_capture.c src/libwim_la-test_support.lo: src/test_support.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -MT src/libwim_la-test_support.lo -MD -MP -MF src/$(DEPDIR)/libwim_la-test_support.Tpo -c -o src/libwim_la-test_support.lo `test -f 'src/test_support.c' || echo '$(srcdir)/'`src/test_support.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libwim_la-test_support.Tpo src/$(DEPDIR)/libwim_la-test_support.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/test_support.c' object='src/libwim_la-test_support.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwim_la_CFLAGS) $(CFLAGS) -c -o src/libwim_la-test_support.lo `test -f 'src/test_support.c' || echo '$(srcdir)/'`src/test_support.c programs/wimlib_imagex-imagex.o: programs/imagex.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wimlib_imagex_CFLAGS) $(CFLAGS) -MT programs/wimlib_imagex-imagex.o -MD -MP -MF programs/$(DEPDIR)/wimlib_imagex-imagex.Tpo -c -o programs/wimlib_imagex-imagex.o `test -f 'programs/imagex.c' || echo '$(srcdir)/'`programs/imagex.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) programs/$(DEPDIR)/wimlib_imagex-imagex.Tpo programs/$(DEPDIR)/wimlib_imagex-imagex.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='programs/imagex.c' object='programs/wimlib_imagex-imagex.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wimlib_imagex_CFLAGS) $(CFLAGS) -c -o programs/wimlib_imagex-imagex.o `test -f 'programs/imagex.c' || echo '$(srcdir)/'`programs/imagex.c programs/wimlib_imagex-imagex.obj: programs/imagex.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wimlib_imagex_CFLAGS) $(CFLAGS) -MT programs/wimlib_imagex-imagex.obj -MD -MP -MF programs/$(DEPDIR)/wimlib_imagex-imagex.Tpo -c -o programs/wimlib_imagex-imagex.obj `if test -f 'programs/imagex.c'; then $(CYGPATH_W) 'programs/imagex.c'; else $(CYGPATH_W) '$(srcdir)/programs/imagex.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) programs/$(DEPDIR)/wimlib_imagex-imagex.Tpo programs/$(DEPDIR)/wimlib_imagex-imagex.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='programs/imagex.c' object='programs/wimlib_imagex-imagex.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wimlib_imagex_CFLAGS) $(CFLAGS) -c -o programs/wimlib_imagex-imagex.obj `if test -f 'programs/imagex.c'; then $(CYGPATH_W) 'programs/imagex.c'; else $(CYGPATH_W) '$(srcdir)/programs/imagex.c'; fi` programs/wimlib_imagex-imagex-win32.o: programs/imagex-win32.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wimlib_imagex_CFLAGS) $(CFLAGS) -MT programs/wimlib_imagex-imagex-win32.o -MD -MP -MF programs/$(DEPDIR)/wimlib_imagex-imagex-win32.Tpo -c -o programs/wimlib_imagex-imagex-win32.o `test -f 'programs/imagex-win32.c' || echo '$(srcdir)/'`programs/imagex-win32.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) programs/$(DEPDIR)/wimlib_imagex-imagex-win32.Tpo programs/$(DEPDIR)/wimlib_imagex-imagex-win32.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='programs/imagex-win32.c' object='programs/wimlib_imagex-imagex-win32.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wimlib_imagex_CFLAGS) $(CFLAGS) -c -o programs/wimlib_imagex-imagex-win32.o `test -f 'programs/imagex-win32.c' || echo '$(srcdir)/'`programs/imagex-win32.c programs/wimlib_imagex-imagex-win32.obj: programs/imagex-win32.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wimlib_imagex_CFLAGS) $(CFLAGS) -MT programs/wimlib_imagex-imagex-win32.obj -MD -MP -MF programs/$(DEPDIR)/wimlib_imagex-imagex-win32.Tpo -c -o programs/wimlib_imagex-imagex-win32.obj `if test -f 'programs/imagex-win32.c'; then $(CYGPATH_W) 'programs/imagex-win32.c'; else $(CYGPATH_W) '$(srcdir)/programs/imagex-win32.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) programs/$(DEPDIR)/wimlib_imagex-imagex-win32.Tpo programs/$(DEPDIR)/wimlib_imagex-imagex-win32.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='programs/imagex-win32.c' object='programs/wimlib_imagex-imagex-win32.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wimlib_imagex_CFLAGS) $(CFLAGS) -c -o programs/wimlib_imagex-imagex-win32.obj `if test -f 'programs/imagex-win32.c'; then $(CYGPATH_W) 'programs/imagex-win32.c'; else $(CYGPATH_W) '$(srcdir)/programs/imagex-win32.c'; fi` programs/wimlib_imagex-wgetopt.o: programs/wgetopt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wimlib_imagex_CFLAGS) $(CFLAGS) -MT programs/wimlib_imagex-wgetopt.o -MD -MP -MF programs/$(DEPDIR)/wimlib_imagex-wgetopt.Tpo -c -o programs/wimlib_imagex-wgetopt.o `test -f 'programs/wgetopt.c' || echo '$(srcdir)/'`programs/wgetopt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) programs/$(DEPDIR)/wimlib_imagex-wgetopt.Tpo programs/$(DEPDIR)/wimlib_imagex-wgetopt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='programs/wgetopt.c' object='programs/wimlib_imagex-wgetopt.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wimlib_imagex_CFLAGS) $(CFLAGS) -c -o programs/wimlib_imagex-wgetopt.o `test -f 'programs/wgetopt.c' || echo '$(srcdir)/'`programs/wgetopt.c programs/wimlib_imagex-wgetopt.obj: programs/wgetopt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wimlib_imagex_CFLAGS) $(CFLAGS) -MT programs/wimlib_imagex-wgetopt.obj -MD -MP -MF programs/$(DEPDIR)/wimlib_imagex-wgetopt.Tpo -c -o programs/wimlib_imagex-wgetopt.obj `if test -f 'programs/wgetopt.c'; then $(CYGPATH_W) 'programs/wgetopt.c'; else $(CYGPATH_W) '$(srcdir)/programs/wgetopt.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) programs/$(DEPDIR)/wimlib_imagex-wgetopt.Tpo programs/$(DEPDIR)/wimlib_imagex-wgetopt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='programs/wgetopt.c' object='programs/wimlib_imagex-wgetopt.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(wimlib_imagex_CFLAGS) $(CFLAGS) -c -o programs/wimlib_imagex-wgetopt.obj `if test -f 'programs/wgetopt.c'; then $(CYGPATH_W) 'programs/wgetopt.c'; else $(CYGPATH_W) '$(srcdir)/programs/wgetopt.c'; fi` mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs -rm -rf src/.libs src/_libs -rm -rf tests/.libs tests/_libs distclean-libtool: -rm -f libtool config.lt install-man1: $(man1_MANS) @$(NORMAL_INSTALL) @list1='$(man1_MANS)'; \ list2=''; \ test -n "$(man1dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.1[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \ done; } uninstall-man1: @$(NORMAL_UNINSTALL) @list='$(man1_MANS)'; test -n "$(man1dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir) install-pkgconfigDATA: $(pkgconfig_DATA) @$(NORMAL_INSTALL) @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ done uninstall-pkgconfigDATA: @$(NORMAL_UNINSTALL) @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) install-includeHEADERS: $(include_HEADERS) @$(NORMAL_INSTALL) @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \ done uninstall-includeHEADERS: @$(NORMAL_UNINSTALL) @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscope: cscope.files test ! -s cscope.files \ || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) clean-cscope: -rm -f cscope.files cscope.files: clean-cscope cscopelist cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -rm -f cscope.out cscope.in.out cscope.po.out cscope.files # Recover from deleted '.trs' file; this should ensure that # "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create # both 'foo.log' and 'foo.trs'. Break the recipe in two subshells # to avoid problems with "make -n". .log.trs: rm -f $< $@ $(MAKE) $(AM_MAKEFLAGS) $< # Leading 'am--fnord' is there to ensure the list of targets does not # expand to empty, as could happen e.g. with make check TESTS=''. am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) am--force-recheck: @: $(TEST_SUITE_LOG): $(TEST_LOGS) @$(am__set_TESTS_bases); \ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ redo_bases=`for i in $$bases; do \ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ done`; \ if test -n "$$redo_bases"; then \ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ if $(am__make_dryrun); then :; else \ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ fi; \ fi; \ if test -n "$$am__remaking_logs"; then \ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ "recursion detected" >&2; \ elif test -n "$$redo_logs"; then \ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ fi; \ if $(am__make_dryrun); then :; else \ st=0; \ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ for i in $$redo_bases; do \ test -f $$i.trs && test -r $$i.trs \ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ test -f $$i.log && test -r $$i.log \ || { echo "$$errmsg $$i.log" >&2; st=1; }; \ done; \ test $$st -eq 0 || exit 1; \ fi @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ ws='[ ]'; \ results=`for b in $$bases; do echo $$b.trs; done`; \ test -n "$$results" || results=/dev/null; \ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ if test `expr $$fail + $$xpass + $$error` -eq 0; then \ success=true; \ else \ success=false; \ fi; \ br='==================='; br=$$br$$br$$br$$br; \ result_count () \ { \ if test x"$$1" = x"--maybe-color"; then \ maybe_colorize=yes; \ elif test x"$$1" = x"--no-color"; then \ maybe_colorize=no; \ else \ echo "$@: invalid 'result_count' usage" >&2; exit 4; \ fi; \ shift; \ desc=$$1 count=$$2; \ if test $$maybe_colorize = yes && test $$count -gt 0; then \ color_start=$$3 color_end=$$std; \ else \ color_start= color_end=; \ fi; \ echo "$${color_start}# $$desc $$count$${color_end}"; \ }; \ create_testsuite_report () \ { \ result_count $$1 "TOTAL:" $$all "$$brg"; \ result_count $$1 "PASS: " $$pass "$$grn"; \ result_count $$1 "SKIP: " $$skip "$$blu"; \ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ result_count $$1 "FAIL: " $$fail "$$red"; \ result_count $$1 "XPASS:" $$xpass "$$red"; \ result_count $$1 "ERROR:" $$error "$$mgn"; \ }; \ { \ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ $(am__rst_title); \ create_testsuite_report --no-color; \ echo; \ echo ".. contents:: :depth: 2"; \ echo; \ for b in $$bases; do echo $$b; done \ | $(am__create_global_log); \ } >$(TEST_SUITE_LOG).tmp || exit 1; \ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ if $$success; then \ col="$$grn"; \ else \ col="$$red"; \ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ fi; \ echo "$${col}$$br$${std}"; \ echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ echo "$${col}$$br$${std}"; \ create_testsuite_report --maybe-color; \ echo "$$col$$br$$std"; \ if $$success; then :; else \ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ if test -n "$(PACKAGE_BUGREPORT)"; then \ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ fi; \ echo "$$col$$br$$std"; \ fi; \ $$success || exit 1 check-TESTS: $(check_PROGRAMS) $(dist_check_SCRIPTS) @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) @set +e; $(am__set_TESTS_bases); \ log_list=`for i in $$bases; do echo $$i.log; done`; \ trs_list=`for i in $$bases; do echo $$i.trs; done`; \ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ exit $$?; recheck: all $(check_PROGRAMS) $(dist_check_SCRIPTS) @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) @set +e; $(am__set_TESTS_bases); \ bases=`for i in $$bases; do echo $$i; done \ | $(am__list_recheck_tests)` || exit 1; \ log_list=`for i in $$bases; do echo $$i.log; done`; \ log_list=`echo $$log_list`; \ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ am__force_recheck=am--force-recheck \ TEST_LOGS="$$log_list"; \ exit $$? tests/test-imagex.log: tests/test-imagex @p='tests/test-imagex'; \ b='tests/test-imagex'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) tests/test-imagex-capture_and_apply.log: tests/test-imagex-capture_and_apply @p='tests/test-imagex-capture_and_apply'; \ b='tests/test-imagex-capture_and_apply'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) tests/test-imagex-update_and_extract.log: tests/test-imagex-update_and_extract @p='tests/test-imagex-update_and_extract'; \ b='tests/test-imagex-update_and_extract'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) tests/test-imagex-mount.log: tests/test-imagex-mount @p='tests/test-imagex-mount'; \ b='tests/test-imagex-mount'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) tests/test-imagex-ntfs.log: tests/test-imagex-ntfs @p='tests/test-imagex-ntfs'; \ b='tests/test-imagex-ntfs'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) .test.log: @p='$<'; \ $(am__set_b); \ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) @am__EXEEXT_TRUE@.test$(EXEEXT).log: @am__EXEEXT_TRUE@ @p='$<'; \ @am__EXEEXT_TRUE@ $(am__set_b); \ @am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ @am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ @am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ @am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done -test -n "$(am__skip_mode_fix)" \ || find "$(distdir)" -type d ! -perm -755 \ -exec chmod u+rwx,go+rx {} \; -o \ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r "$(distdir)" dist-gzip: distdir tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz $(am__post_remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 $(am__post_remove_distdir) dist-lzip: distdir tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz $(am__post_remove_distdir) dist-xz: distdir tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz $(am__post_remove_distdir) dist-tarZ: distdir @echo WARNING: "Support for distribution archives compressed with" \ "legacy program 'compress' is deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z $(am__post_remove_distdir) dist-shar: distdir @echo WARNING: "Support for shar distribution archives is" \ "deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz $(am__post_remove_distdir) dist-zip: distdir -rm -f $(distdir).zip zip -rq $(distdir).zip $(distdir) $(am__post_remove_distdir) dist dist-all: $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' $(am__post_remove_distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another # tarfile. distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lz*) \ lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ esac chmod -R a-w $(distdir) chmod u+w $(distdir) mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && am__cwd=`pwd` \ && $(am__cd) $(distdir)/_build/sub \ && ../../configure \ $(AM_DISTCHECK_CONFIGURE_FLAGS) \ $(DISTCHECK_CONFIGURE_FLAGS) \ --srcdir=../.. --prefix="$$dc_install_base" \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) dvi \ && $(MAKE) $(AM_MAKEFLAGS) check \ && $(MAKE) $(AM_MAKEFLAGS) install \ && $(MAKE) $(AM_MAKEFLAGS) installcheck \ && $(MAKE) $(AM_MAKEFLAGS) uninstall \ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ distuninstallcheck \ && chmod -R a-w "$$dc_install_base" \ && ({ \ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ } || { rm -rf "$$dc_destdir"; exit 1; }) \ && rm -rf "$$dc_destdir" \ && $(MAKE) $(AM_MAKEFLAGS) dist \ && rm -rf $(DIST_ARCHIVES) \ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ && cd "$$am__cwd" \ || exit 1 $(am__post_remove_distdir) @(echo "$(distdir) archives ready for distribution: "; \ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' distuninstallcheck: @test -n '$(distuninstallcheck_dir)' || { \ echo 'ERROR: trying to run $@ with an empty' \ '$$(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ $(am__cd) '$(distuninstallcheck_dir)' || { \ echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left after uninstall:" ; \ if test -n "$(DESTDIR)"; then \ echo " (check DESTDIR support)"; \ fi ; \ $(distuninstallcheck_listfiles) ; \ exit 1; } >&2 distcleancheck: distclean @if test '$(srcdir)' = . ; then \ echo "ERROR: distcleancheck can only run from a VPATH build" ; \ exit 1 ; \ fi @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left in build directory after distclean:" ; \ $(distcleancheck_listfiles) ; \ exit 1; } >&2 check-am: all-am $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) \ $(dist_check_SCRIPTS) $(MAKE) $(AM_MAKEFLAGS) check-TESTS check: check-am all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(SCRIPTS) $(MANS) $(DATA) \ $(HEADERS) config.h install-binPROGRAMS: install-libLTLIBRARIES installdirs: for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(includedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -rm -f programs/$(DEPDIR)/$(am__dirstamp) -rm -f programs/$(am__dirstamp) -rm -f src/$(DEPDIR)/$(am__dirstamp) -rm -f src/$(am__dirstamp) -rm -f tests/$(DEPDIR)/$(am__dirstamp) -rm -f tests/$(am__dirstamp) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-generic \ clean-libLTLIBRARIES clean-libtool mostlyclean-am distclean: distclean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -f programs/$(DEPDIR)/wimlib_imagex-imagex-win32.Po -rm -f programs/$(DEPDIR)/wimlib_imagex-imagex.Po -rm -f programs/$(DEPDIR)/wimlib_imagex-wgetopt.Po -rm -f src/$(DEPDIR)/libwim_la-add_image.Plo -rm -f src/$(DEPDIR)/libwim_la-avl_tree.Plo -rm -f src/$(DEPDIR)/libwim_la-blob_table.Plo -rm -f src/$(DEPDIR)/libwim_la-compress.Plo -rm -f src/$(DEPDIR)/libwim_la-compress_common.Plo -rm -f src/$(DEPDIR)/libwim_la-compress_parallel.Plo -rm -f src/$(DEPDIR)/libwim_la-compress_serial.Plo -rm -f src/$(DEPDIR)/libwim_la-decompress.Plo -rm -f src/$(DEPDIR)/libwim_la-decompress_common.Plo -rm -f src/$(DEPDIR)/libwim_la-delete_image.Plo -rm -f src/$(DEPDIR)/libwim_la-dentry.Plo -rm -f src/$(DEPDIR)/libwim_la-divsufsort.Plo -rm -f src/$(DEPDIR)/libwim_la-encoding.Plo -rm -f src/$(DEPDIR)/libwim_la-error.Plo -rm -f src/$(DEPDIR)/libwim_la-export_image.Plo -rm -f src/$(DEPDIR)/libwim_la-extract.Plo -rm -f src/$(DEPDIR)/libwim_la-file_io.Plo -rm -f src/$(DEPDIR)/libwim_la-header.Plo -rm -f src/$(DEPDIR)/libwim_la-inode.Plo -rm -f src/$(DEPDIR)/libwim_la-inode_fixup.Plo -rm -f src/$(DEPDIR)/libwim_la-inode_table.Plo -rm -f src/$(DEPDIR)/libwim_la-integrity.Plo -rm -f src/$(DEPDIR)/libwim_la-iterate_dir.Plo -rm -f src/$(DEPDIR)/libwim_la-join.Plo -rm -f src/$(DEPDIR)/libwim_la-lcpit_matchfinder.Plo -rm -f src/$(DEPDIR)/libwim_la-lzms_common.Plo -rm -f src/$(DEPDIR)/libwim_la-lzms_compress.Plo -rm -f src/$(DEPDIR)/libwim_la-lzms_decompress.Plo -rm -f src/$(DEPDIR)/libwim_la-lzx_common.Plo -rm -f src/$(DEPDIR)/libwim_la-lzx_compress.Plo -rm -f src/$(DEPDIR)/libwim_la-lzx_decompress.Plo -rm -f src/$(DEPDIR)/libwim_la-metadata_resource.Plo -rm -f src/$(DEPDIR)/libwim_la-mount_image.Plo -rm -f src/$(DEPDIR)/libwim_la-ntfs-3g_apply.Plo -rm -f src/$(DEPDIR)/libwim_la-ntfs-3g_capture.Plo -rm -f src/$(DEPDIR)/libwim_la-pathlist.Plo -rm -f src/$(DEPDIR)/libwim_la-paths.Plo -rm -f src/$(DEPDIR)/libwim_la-pattern.Plo -rm -f src/$(DEPDIR)/libwim_la-progress.Plo -rm -f src/$(DEPDIR)/libwim_la-reference.Plo -rm -f src/$(DEPDIR)/libwim_la-registry.Plo -rm -f src/$(DEPDIR)/libwim_la-reparse.Plo -rm -f src/$(DEPDIR)/libwim_la-resource.Plo -rm -f src/$(DEPDIR)/libwim_la-scan.Plo -rm -f src/$(DEPDIR)/libwim_la-security.Plo -rm -f src/$(DEPDIR)/libwim_la-sha1.Plo -rm -f src/$(DEPDIR)/libwim_la-solid.Plo -rm -f src/$(DEPDIR)/libwim_la-split.Plo -rm -f src/$(DEPDIR)/libwim_la-tagged_items.Plo -rm -f src/$(DEPDIR)/libwim_la-template.Plo -rm -f src/$(DEPDIR)/libwim_la-test_support.Plo -rm -f src/$(DEPDIR)/libwim_la-textfile.Plo -rm -f src/$(DEPDIR)/libwim_la-timestamp.Plo -rm -f src/$(DEPDIR)/libwim_la-unix_apply.Plo -rm -f src/$(DEPDIR)/libwim_la-unix_capture.Plo -rm -f src/$(DEPDIR)/libwim_la-update_image.Plo -rm -f src/$(DEPDIR)/libwim_la-util.Plo -rm -f src/$(DEPDIR)/libwim_la-verify.Plo -rm -f src/$(DEPDIR)/libwim_la-wim.Plo -rm -f src/$(DEPDIR)/libwim_la-wimboot.Plo -rm -f src/$(DEPDIR)/libwim_la-win32_apply.Plo -rm -f src/$(DEPDIR)/libwim_la-win32_capture.Plo -rm -f src/$(DEPDIR)/libwim_la-win32_common.Plo -rm -f src/$(DEPDIR)/libwim_la-win32_replacements.Plo -rm -f src/$(DEPDIR)/libwim_la-win32_vss.Plo -rm -f src/$(DEPDIR)/libwim_la-write.Plo -rm -f src/$(DEPDIR)/libwim_la-x86_cpu_features.Plo -rm -f src/$(DEPDIR)/libwim_la-xml.Plo -rm -f src/$(DEPDIR)/libwim_la-xml_windows.Plo -rm -f src/$(DEPDIR)/libwim_la-xpress_compress.Plo -rm -f src/$(DEPDIR)/libwim_la-xpress_decompress.Plo -rm -f tests/$(DEPDIR)/tree-cmp.Po -rm -f tests/$(DEPDIR)/wlfuzz.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-hdr distclean-libtool distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-includeHEADERS install-man \ install-pkgconfigDATA @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) install-data-hook install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-binPROGRAMS install-dist_binSCRIPTS \ install-libLTLIBRARIES @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) install-exec-hook install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-man1 install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache -rm -f programs/$(DEPDIR)/wimlib_imagex-imagex-win32.Po -rm -f programs/$(DEPDIR)/wimlib_imagex-imagex.Po -rm -f programs/$(DEPDIR)/wimlib_imagex-wgetopt.Po -rm -f src/$(DEPDIR)/libwim_la-add_image.Plo -rm -f src/$(DEPDIR)/libwim_la-avl_tree.Plo -rm -f src/$(DEPDIR)/libwim_la-blob_table.Plo -rm -f src/$(DEPDIR)/libwim_la-compress.Plo -rm -f src/$(DEPDIR)/libwim_la-compress_common.Plo -rm -f src/$(DEPDIR)/libwim_la-compress_parallel.Plo -rm -f src/$(DEPDIR)/libwim_la-compress_serial.Plo -rm -f src/$(DEPDIR)/libwim_la-decompress.Plo -rm -f src/$(DEPDIR)/libwim_la-decompress_common.Plo -rm -f src/$(DEPDIR)/libwim_la-delete_image.Plo -rm -f src/$(DEPDIR)/libwim_la-dentry.Plo -rm -f src/$(DEPDIR)/libwim_la-divsufsort.Plo -rm -f src/$(DEPDIR)/libwim_la-encoding.Plo -rm -f src/$(DEPDIR)/libwim_la-error.Plo -rm -f src/$(DEPDIR)/libwim_la-export_image.Plo -rm -f src/$(DEPDIR)/libwim_la-extract.Plo -rm -f src/$(DEPDIR)/libwim_la-file_io.Plo -rm -f src/$(DEPDIR)/libwim_la-header.Plo -rm -f src/$(DEPDIR)/libwim_la-inode.Plo -rm -f src/$(DEPDIR)/libwim_la-inode_fixup.Plo -rm -f src/$(DEPDIR)/libwim_la-inode_table.Plo -rm -f src/$(DEPDIR)/libwim_la-integrity.Plo -rm -f src/$(DEPDIR)/libwim_la-iterate_dir.Plo -rm -f src/$(DEPDIR)/libwim_la-join.Plo -rm -f src/$(DEPDIR)/libwim_la-lcpit_matchfinder.Plo -rm -f src/$(DEPDIR)/libwim_la-lzms_common.Plo -rm -f src/$(DEPDIR)/libwim_la-lzms_compress.Plo -rm -f src/$(DEPDIR)/libwim_la-lzms_decompress.Plo -rm -f src/$(DEPDIR)/libwim_la-lzx_common.Plo -rm -f src/$(DEPDIR)/libwim_la-lzx_compress.Plo -rm -f src/$(DEPDIR)/libwim_la-lzx_decompress.Plo -rm -f src/$(DEPDIR)/libwim_la-metadata_resource.Plo -rm -f src/$(DEPDIR)/libwim_la-mount_image.Plo -rm -f src/$(DEPDIR)/libwim_la-ntfs-3g_apply.Plo -rm -f src/$(DEPDIR)/libwim_la-ntfs-3g_capture.Plo -rm -f src/$(DEPDIR)/libwim_la-pathlist.Plo -rm -f src/$(DEPDIR)/libwim_la-paths.Plo -rm -f src/$(DEPDIR)/libwim_la-pattern.Plo -rm -f src/$(DEPDIR)/libwim_la-progress.Plo -rm -f src/$(DEPDIR)/libwim_la-reference.Plo -rm -f src/$(DEPDIR)/libwim_la-registry.Plo -rm -f src/$(DEPDIR)/libwim_la-reparse.Plo -rm -f src/$(DEPDIR)/libwim_la-resource.Plo -rm -f src/$(DEPDIR)/libwim_la-scan.Plo -rm -f src/$(DEPDIR)/libwim_la-security.Plo -rm -f src/$(DEPDIR)/libwim_la-sha1.Plo -rm -f src/$(DEPDIR)/libwim_la-solid.Plo -rm -f src/$(DEPDIR)/libwim_la-split.Plo -rm -f src/$(DEPDIR)/libwim_la-tagged_items.Plo -rm -f src/$(DEPDIR)/libwim_la-template.Plo -rm -f src/$(DEPDIR)/libwim_la-test_support.Plo -rm -f src/$(DEPDIR)/libwim_la-textfile.Plo -rm -f src/$(DEPDIR)/libwim_la-timestamp.Plo -rm -f src/$(DEPDIR)/libwim_la-unix_apply.Plo -rm -f src/$(DEPDIR)/libwim_la-unix_capture.Plo -rm -f src/$(DEPDIR)/libwim_la-update_image.Plo -rm -f src/$(DEPDIR)/libwim_la-util.Plo -rm -f src/$(DEPDIR)/libwim_la-verify.Plo -rm -f src/$(DEPDIR)/libwim_la-wim.Plo -rm -f src/$(DEPDIR)/libwim_la-wimboot.Plo -rm -f src/$(DEPDIR)/libwim_la-win32_apply.Plo -rm -f src/$(DEPDIR)/libwim_la-win32_capture.Plo -rm -f src/$(DEPDIR)/libwim_la-win32_common.Plo -rm -f src/$(DEPDIR)/libwim_la-win32_replacements.Plo -rm -f src/$(DEPDIR)/libwim_la-win32_vss.Plo -rm -f src/$(DEPDIR)/libwim_la-write.Plo -rm -f src/$(DEPDIR)/libwim_la-x86_cpu_features.Plo -rm -f src/$(DEPDIR)/libwim_la-xml.Plo -rm -f src/$(DEPDIR)/libwim_la-xml_windows.Plo -rm -f src/$(DEPDIR)/libwim_la-xpress_compress.Plo -rm -f src/$(DEPDIR)/libwim_la-xpress_decompress.Plo -rm -f tests/$(DEPDIR)/tree-cmp.Po -rm -f tests/$(DEPDIR)/wlfuzz.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-binPROGRAMS uninstall-dist_binSCRIPTS \ uninstall-includeHEADERS uninstall-libLTLIBRARIES \ uninstall-man uninstall-pkgconfigDATA @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) uninstall-hook uninstall-man: uninstall-man1 .MAKE: all check-am install-am install-data-am install-exec-am \ install-strip uninstall-am .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles am--refresh check \ check-TESTS check-am clean clean-binPROGRAMS \ clean-checkPROGRAMS clean-cscope clean-generic \ clean-libLTLIBRARIES clean-libtool cscope cscopelist-am ctags \ ctags-am dist dist-all dist-bzip2 dist-gzip dist-lzip \ dist-shar dist-tarZ dist-xz dist-zip distcheck distclean \ distclean-compile distclean-generic distclean-hdr \ distclean-libtool distclean-tags distcleancheck distdir \ distuninstallcheck dvi dvi-am html html-am info info-am \ install install-am install-binPROGRAMS install-data \ install-data-am install-data-hook install-dist_binSCRIPTS \ install-dvi install-dvi-am install-exec install-exec-am \ install-exec-hook install-html install-html-am \ install-includeHEADERS install-info install-info-am \ install-libLTLIBRARIES install-man install-man1 install-pdf \ install-pdf-am install-pkgconfigDATA install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am recheck tags tags-am uninstall \ uninstall-am uninstall-binPROGRAMS uninstall-dist_binSCRIPTS \ uninstall-hook uninstall-includeHEADERS \ uninstall-libLTLIBRARIES uninstall-man uninstall-man1 \ uninstall-pkgconfigDATA .PRECIOUS: Makefile wimlib.pc: config.status @ENABLE_SSSE3_SHA1_TRUE@src/sha1-ssse3.lo:src/sha1-ssse3.asm @ENABLE_SSSE3_SHA1_TRUE@ $(LIBTOOL) --mode=compile $(srcdir)/build-aux/nasm_lt.sh \ @ENABLE_SSSE3_SHA1_TRUE@ $(NASM) $(NAFLAGS) $(NASM_PLATFORM_FLAGS) \ @ENABLE_SSSE3_SHA1_TRUE@ -DINTEL_SHA1_UPDATE_FUNCNAME=$(NASM_SYMBOL_PREFIX)sha1_transform_blocks_ssse3 \ @ENABLE_SSSE3_SHA1_TRUE@ -DINTEL_SHA1_UPDATE_DEFAULT_DISPATCH=$(NASM_SYMBOL_PREFIX)sha1_transform_blocks_default \ @ENABLE_SSSE3_SHA1_TRUE@ $< -o $@ ############################################################################## # Hooks # ############################################################################## install-exec-hook: for cmd in $(wimlib_imagex_cmds); do \ cd $(DESTDIR)$(bindir) && \ ln -f wimlib-imagex wim$${cmd}; \ done install-data-hook: for cmd in $(wimlib_imagex_cmds); do \ cd $(DESTDIR)$(mandir)/man1 && \ ln -sf wim$${cmd}.1 wimlib-imagex-$${cmd}.1; \ done uninstall-hook: for cmd in $(wimlib_imagex_cmds); do \ rm -f $(DESTDIR)$(bindir)/wim$${cmd}; \ rm -f $(DESTDIR)$(mandir)/man1/wim$${cmd}.1; \ done ############################################################################## # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: wimlib-1.13.1/examples/0000755000175000017500000000000013160354224011677 500000000000000wimlib-1.13.1/examples/decompressfile.c0000644000175000017500000001373413160354224014777 00000000000000/* * decompressfile.c - decompression API example * * The following copying information applies to this specific source code file: * * Written in 2014-2016 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . */ /* * This an example of using wimlib's compression API to decompress a file * compressed with the compressfile.c program. * * This program does *not* have anything to do with WIM files other than the * fact that this makes use of compression formats that are used in WIM files. * This is purely an example of using the compression API. * * Compile with: * * $ gcc decompressfile.c -o decompressfile -lwim * * Run with: * * $ ./decompressfile INFILE OUTFILE * * For example: * * $ ./compressfile book.txt book.txt.lzms LZMS 1048576 * $ rm -f book.txt * $ ./decompressfile book.txt.lzms book.txt * * The compressed file format created here is simply a series of compressed * chunks. A real format would need to have checksums and other metadata. */ #define _FILE_OFFSET_BITS 64 #if defined(_MSC_VER) && _MSC_VER < 1800 /* VS pre-2013? */ # define PRIu64 "I64u" # define PRIu32 "u" #else # define __STDC_FORMAT_MACROS 1 # include #endif #include #include #include #include #include #include #ifdef _WIN32 # include #else # include #endif /* * Windows compatibility defines for string encoding. Applications using wimlib * that need to run on both UNIX and Windows will need to do something similar * to this, whereas applications that only need to run on one or the other can * just use their platform's convention directly. */ #ifdef _WIN32 # define main wmain typedef wchar_t tchar; # define TS "ls" # define topen _wopen #else typedef char tchar; # define TS "s" # define topen open # define O_BINARY 0 #endif static void fatal_error(int err, const char *format, ...) { va_list va; va_start(va, format); vfprintf(stderr, format, va); if (err != 0) fprintf(stderr, ": %s\n", strerror(err)); else fputc('\n', stderr); va_end(va); exit(1); } static void do_decompress(int in_fd, const tchar *in_filename, int out_fd, const tchar *out_filename, uint32_t chunk_size, struct wimlib_decompressor *decompressor) { uint64_t chunk_num; char *ubuf = (char *)malloc(chunk_size); char *cbuf = (char *)malloc(chunk_size - 1); for (chunk_num = 1; ; chunk_num++) { int32_t bytes_read; uint32_t usize; uint32_t csize; /* Read chunk uncompressed and compressed sizes. */ bytes_read = read(in_fd, &usize, sizeof(uint32_t)); if (bytes_read == 0) break; if (bytes_read != sizeof(uint32_t) || read(in_fd, &csize, sizeof(uint32_t)) != sizeof(uint32_t)) { fatal_error(errno, "Error reading \"%" TS"\"", in_filename); } if (csize > usize || usize > chunk_size) fatal_error(0, "The data is invalid!"); if (usize == csize) { if (read(in_fd, ubuf, usize) != (int32_t)usize) { fatal_error(errno, "Error reading \"%" TS"\"", in_filename); } } else { if (read(in_fd, cbuf, csize) != (int32_t)csize) { fatal_error(errno, "Error reading \"%" TS"\"", in_filename); } if (wimlib_decompress(cbuf, csize, ubuf, usize, decompressor)) { fatal_error(0, "The compressed data is invalid!"); } } printf("Chunk %" PRIu64": %" PRIu32" => %" PRIu32" bytes\n", chunk_num, csize, usize); /* Output the uncompressed chunk size, the compressed chunk * size, then the chunk data. Note: a real program would need * to output the chunk sizes in consistent endianness. */ if (write(out_fd, ubuf, usize) != (int32_t)usize) { fatal_error(errno, "Error writing to \"%" TS"\"", out_filename); } } free(ubuf); free(cbuf); } int main(int argc, tchar **argv) { const tchar *in_filename; const tchar *out_filename; int in_fd; int out_fd; uint32_t ctype32; enum wimlib_compression_type ctype; uint32_t chunk_size; int ret; struct wimlib_decompressor *decompressor; if (argc != 3) { fprintf(stderr, "Usage: %" TS" INFILE OUTFILE\n", argv[0]); return 2; } in_filename = argv[1]; out_filename = argv[2]; /* Open input file and output file. */ in_fd = topen(in_filename, O_RDONLY | O_BINARY); if (in_fd < 0) fatal_error(errno, "Failed to open \"%" TS"\"", in_filename); out_fd = topen(out_filename, O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0644); if (out_fd < 0) fatal_error(errno, "Failed to open \"%" TS"\"", out_filename); /* Get compression type and chunk size. */ if (read(in_fd, &ctype32, sizeof(uint32_t)) != sizeof(uint32_t) || read(in_fd, &chunk_size, sizeof(uint32_t)) != sizeof(uint32_t)) fatal_error(errno, "Error reading from \"%" TS"\"", in_filename); ctype = (enum wimlib_compression_type)ctype32; /* Create a decompressor for the compression type and chunk size with * the default parameters. */ ret = wimlib_create_decompressor(ctype, chunk_size, &decompressor); if (ret != 0) fatal_error(0, "Failed to create decompressor: %" TS, wimlib_get_error_string((enum wimlib_error_code)ret)); /* Decompress and write the data. */ do_decompress(in_fd, in_filename, out_fd, out_filename, chunk_size, decompressor); /* Cleanup and return. */ if (close(out_fd)) fatal_error(errno, "Error closing \"%" TS"\"", out_filename); wimlib_free_decompressor(decompressor); return 0; } wimlib-1.13.1/examples/applywim.c0000644000175000017500000000620713160354224013632 00000000000000/* * applywim.c - A program to extract the first image from a WIM file. * * The following copying information applies to this specific source code file: * * Written in 2013-2016 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . */ #include #include /* * Windows compatibility defines for string encoding. Applications using wimlib * that need to run on both UNIX and Windows will need to do something similar * to this, whereas applications that only need to run on one or the other can * just use their platform's convention directly. */ #ifdef _WIN32 # define main wmain typedef wchar_t tchar; # define TS "ls" #else typedef char tchar; # define TS "s" #endif #define TO_PERCENT(numerator, denominator) \ ((float)(((denominator) == 0) ? 0 : ((numerator) * 100 / (float)(denominator)))) static enum wimlib_progress_status extract_progress(enum wimlib_progress_msg msg, union wimlib_progress_info *info, void *progctx) { switch (msg) { case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS: printf("Extracting files: %.2f%% complete\n", TO_PERCENT(info->extract.completed_bytes, info->extract.total_bytes)); break; default: break; } return WIMLIB_PROGRESS_STATUS_CONTINUE; } int main(int argc, tchar **argv) { int ret; WIMStruct *wim = NULL; const tchar *wimpath; const tchar *destdir; /* Check for the correct number of arguments. */ if (argc != 3) { fprintf(stderr, "Usage: applywim WIM DIR\n"); return 2; } wimpath = argv[1]; destdir = argv[2]; /* Open the WIM file as a WIMStruct. */ ret = wimlib_open_wim(wimpath, /* Path of WIM file to open */ 0, /* WIMLIB_OPEN_FLAG_* flags (0 means all defaults) */ &wim); /* Return the WIMStruct pointer in this location */ if (ret != 0) /* Always should check the error codes. */ goto out; /* Register our progress function. */ wimlib_register_progress_function(wim, extract_progress, NULL); /* Extract the first image. */ ret = wimlib_extract_image(wim, /* WIMStruct from which to extract the image */ 1, /* Image to extract */ destdir, /* Directory to extract the image to */ 0); /* WIMLIB_EXTRACT_FLAG_* flags (0 means all defaults) */ out: /* Free the WIMStruct. Has no effect if the pointer to it is NULL. */ wimlib_free(wim); /* Check for error status. */ if (ret != 0) { fprintf(stderr, "wimlib error %d: %" TS"\n", ret, wimlib_get_error_string((enum wimlib_error_code)ret)); } /* Free global memory (optional). */ wimlib_global_cleanup(); return ret; } wimlib-1.13.1/examples/capturewim.c0000644000175000017500000000700613160354224014146 00000000000000/* * capturewim.c - A program to capture a directory tree into a WIM file. * * The following copying information applies to this specific source code file: * * Written in 2014-2016 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . */ #include #include /* * Windows compatibility defines for string encoding. Applications using wimlib * that need to run on both UNIX and Windows will need to do something similar * to this, whereas applications that only need to run on one or the other can * just use their platform's convention directly. */ #ifdef _WIN32 # define main wmain typedef wchar_t tchar; # define TS "ls" #else typedef char tchar; # define TS "s" #endif #define TO_PERCENT(numerator, denominator) \ ((float)(((denominator) == 0) ? 0 : ((numerator) * 100 / (float)(denominator)))) static enum wimlib_progress_status write_progress(enum wimlib_progress_msg msg, union wimlib_progress_info *info, void *progctx) { switch (msg) { case WIMLIB_PROGRESS_MSG_WRITE_STREAMS: printf("Writing WIM: %.2f%% complete\n", TO_PERCENT(info->write_streams.completed_bytes, info->write_streams.total_bytes)); break; default: break; } return WIMLIB_PROGRESS_STATUS_CONTINUE; } int main(int argc, tchar **argv) { int ret; WIMStruct *wim = NULL; const tchar *srcdir; const tchar *wimpath; /* Check for the correct number of arguments. */ if (argc != 3) { fprintf(stderr, "Usage: capturewim DIR WIM\n"); return 2; } srcdir = argv[1]; wimpath = argv[2]; /* Create a WIMStruct for a WIM. */ ret = wimlib_create_new_wim(WIMLIB_COMPRESSION_TYPE_LZX, &wim); if (ret != 0) /* Always should check the error codes. */ goto out; /* Register our progress function. */ wimlib_register_progress_function(wim, write_progress, NULL); /* Add the directory tree to the WIMStruct as an image. */ ret = wimlib_add_image(wim, /* WIMStruct to which to add the image */ srcdir, /* Directory from which to add the image */ NULL, /* Name to give the image (NULL means none) */ NULL, /* Capture configuration structure (NULL means none) */ 0); /* WIMLIB_ADD_FLAG_* flags (0 means all defaults) */ if (ret != 0) goto out; /* Write the WIM file. */ ret = wimlib_write(wim, /* WIMStruct from which to write a WIM */ wimpath, /* Path to write the WIM to */ WIMLIB_ALL_IMAGES, /* Image(s) in the WIM to write */ 0, /* WIMLIB_WRITE_FLAG_* flags (0 means all defaults) */ 0); /* Number of compressor threads (0 means default) */ out: /* Free the WIMStruct. Has no effect if the pointer to it is NULL. */ wimlib_free(wim); /* Check for error status. */ if (ret != 0) { fprintf(stderr, "wimlib error %d: %" TS"\n", ret, wimlib_get_error_string((enum wimlib_error_code)ret)); } /* Free global memory (optional). */ wimlib_global_cleanup(); return ret; } wimlib-1.13.1/examples/README0000644000175000017500000000017412260745400012501 00000000000000This directory shows some simple examples of C programs that use wimlib. See programs/imagex.c for a more complete example. wimlib-1.13.1/examples/updatewim.c0000644000175000017500000000761213160354224013770 00000000000000/* * updatewim.c - A program to add a file or directory tree to the first image of * a WIM file. * * The following copying information applies to this specific source code file: * * Written in 2014-2016 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . */ #include #include /* * Windows compatibility defines for string encoding. Applications using wimlib * that need to run on both UNIX and Windows will need to do something similar * to this, whereas applications that only need to run on one or the other can * just use their platform's convention directly. */ #ifdef _WIN32 # define main wmain typedef wchar_t tchar; # define TS "ls" #else typedef char tchar; # define TS "s" #endif int main(int argc, tchar **argv) { int ret; tchar *wimfile; tchar *wim_target_path; tchar *fs_source_path; WIMStruct *wim = NULL; struct wimlib_update_command cmds[1]; /* Check for the correct number of arguments. */ if (argc != 4) { fprintf(stderr, "Usage: updatewim WIMFILE WIM_PATH EXTERNAL_PATH\n"); return 2; } wimfile = argv[1]; wim_target_path = argv[2]; fs_source_path = argv[3]; /* Open the WIM file. */ ret = wimlib_open_wim(wimfile, 0, &wim); if (ret != 0) /* Always should check the error codes. */ goto out; /* Update the WIM image. In this simple example, we add a single file * or directory tree to the specified location in the first image of the * WIM file, using the default options. * * wimlib_add_tree() is actually sufficient for this case, but for the * sake of demonstration we will use the more general function * wimlib_update_image(). */ memset(cmds, 0, sizeof(cmds)); /* Set up an "add" operation. * * Other available operations include WIMLIB_UPDATE_OP_RENAME and * WIMLIB_UPDATE_OP_DELETE. */ cmds[0].op = WIMLIB_UPDATE_OP_ADD; /* Set the arguments to the operation. * * Make sure to fill in 'rename' or 'delete_' instead of 'add' if doing * a rename or delete operation instead! */ cmds[0].add.wim_target_path = wim_target_path; cmds[0].add.fs_source_path = fs_source_path; /* Note: we don't need to explicitly set 'cmds[0].add.config_file' and * 'cmds[0].add.add_flags' because we zeroed the 'struct * wimlib_update_command', and zero means use the defaults. */ ret = wimlib_update_image(wim, /* WIMStruct to update */ 1, /* 1-based index of the image to update */ cmds, /* Array of command structures */ 1, /* Number of command structures in array */ 0); /* WIMLIB_UPDATE_FLAG_* flags (0 for defaults) */ if (ret != 0) goto out; /* Overwrite the WIM file. * * Normally, this will append new data to the file, rather than * rebuilding the entire file. * * Changes do not take effect on-disk until this is done. */ ret = wimlib_overwrite(wim, /* WIMStruct to commit to the underlying file */ 0, /* WIMLIB_WRITE_FLAG_* flags (0 for defaults) */ 0); /* Number of compressor threads (0 means default) */ out: /* Free the WIMStruct. Has no effect if the pointer to it is NULL. */ wimlib_free(wim); /* Check for error status. */ if (ret != 0) { fprintf(stderr, "wimlib error %d: %" TS"\n", ret, wimlib_get_error_string((enum wimlib_error_code)ret)); } /* Free global memory (optional). */ wimlib_global_cleanup(); return ret; } wimlib-1.13.1/examples/compressfile.c0000644000175000017500000001524613160354224014466 00000000000000/* * compressfile.c - compression API example * * The following copying information applies to this specific source code file: * * Written in 2014-2016 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . */ /* * This an example of using wimlib's compression API to compress a file. * * This program does *not* have anything to do with WIM files other than the * fact that this makes use of compression formats that are used in WIM files. * This is purely an example of using the compression API. * * Compile with: * * $ gcc compressfile.c -o compressfile -lwim * * Run with: * * $ ./compressfile INFILE OUTFILE [LZX | XPRESS | LZMS] [chunk size] * * * Use the decompressfile.c program to decompress the file. * * For example: * * $ ./compressfile book.txt book.txt.lzms LZMS 1048576 * $ rm -f book.txt * $ ./decompressfile book.txt.lzms book.txt * * The compressed file format created here is simply a series of compressed * chunks. A real format would need to have checksums and other metadata. */ #define _FILE_OFFSET_BITS 64 #if defined(_MSC_VER) && _MSC_VER < 1800 /* VS pre-2013? */ # define PRIu64 "I64u" # define PRIu32 "u" #else # define __STDC_FORMAT_MACROS 1 # include #endif #include #include #include #include #include #include #ifdef _WIN32 # include #else # include #endif /* * Windows compatibility defines for string encoding. Applications using wimlib * that need to run on both UNIX and Windows will need to do something similar * to this, whereas applications that only need to run on one or the other can * just use their platform's convention directly. */ #ifdef _WIN32 # define main wmain typedef wchar_t tchar; # define _T(text) L##text # define T(text) _T(text) # define TS "ls" # define topen _wopen # define tstrcmp wcscmp # define tstrtol wcstol #else typedef char tchar; # define T(text) text # define TS "s" # define topen open # define O_BINARY 0 # define tstrcmp strcmp # define tstrtol strtol #endif static void fatal_error(int err, const char *format, ...) { va_list va; va_start(va, format); vfprintf(stderr, format, va); if (err != 0) fprintf(stderr, ": %s\n", strerror(err)); else fputc('\n', stderr); va_end(va); exit(1); } static void do_compress(int in_fd, const tchar *in_filename, int out_fd, const tchar *out_filename, uint32_t chunk_size, struct wimlib_compressor *compressor) { char *ubuf = (char *)malloc(chunk_size); char *cbuf = (char *)malloc(chunk_size - 1); uint64_t chunk_num; for (chunk_num = 1; ; chunk_num++) { int32_t bytes_read; size_t csize; char *out_buf; uint32_t out_size; uint32_t usize; /* Read next chunk of data to compress. */ bytes_read = read(in_fd, ubuf, chunk_size); if (bytes_read <= 0) { if (bytes_read == 0) break; fatal_error(errno, "Error reading \"%" TS"\"", in_filename); } /* Compress the chunk. */ usize = bytes_read; csize = wimlib_compress(ubuf, usize, cbuf, usize - 1, compressor); if (csize != 0) { /* Chunk was compressed; use the compressed data. */ out_buf = cbuf; out_size = csize; } else { /* Chunk did not compress to less than original size; * use the uncompressed data. */ out_buf = ubuf; out_size = usize; } printf("Chunk %" PRIu64": %" PRIu32" => %" PRIu32" bytes\n", chunk_num, usize, out_size); /* Output the uncompressed chunk size, the compressed chunk * size, then the chunk data. Note: a real program would need * to output the chunk sizes in consistent endianness. */ if (write(out_fd, &usize, sizeof(uint32_t)) != sizeof(uint32_t) || write(out_fd, &out_size, sizeof(uint32_t)) != sizeof(uint32_t) || write(out_fd, out_buf, out_size) != (int32_t)out_size) { fatal_error(errno, "Error writing to \"%" TS"\"", out_filename); } } free(ubuf); free(cbuf); } int main(int argc, tchar **argv) { const tchar *in_filename; const tchar *out_filename; int in_fd; int out_fd; struct wimlib_compressor *compressor; enum wimlib_compression_type ctype = WIMLIB_COMPRESSION_TYPE_LZX; uint32_t ctype32; uint32_t chunk_size = 32768; int ret; if (argc < 3 || argc > 5) { fprintf(stderr, "Usage: %" TS" INFILE OUTFILE " "[LZX | XPRESS | LZMS] [chunk size]\n", argv[0]); return 2; } in_filename = argv[1]; out_filename = argv[2]; /* Parse compression type (optional) */ if (argc >= 4) { if (!tstrcmp(argv[3], T("LZX"))) ctype = WIMLIB_COMPRESSION_TYPE_LZX; else if (!tstrcmp(argv[3], T("XPRESS"))) ctype = WIMLIB_COMPRESSION_TYPE_XPRESS; else if (!tstrcmp(argv[3], T("LZMS"))) ctype = WIMLIB_COMPRESSION_TYPE_LZMS; else fatal_error(0, "Unrecognized compression type \"%" TS"\"", argv[3]); } /* Parse chunk size (optional). */ if (argc >= 5) chunk_size = tstrtol(argv[4], NULL, 10); /* Open input file and output file. */ in_fd = topen(in_filename, O_RDONLY | O_BINARY); if (in_fd < 0) fatal_error(errno, "Failed to open \"%" TS"\"", in_filename); out_fd = topen(out_filename, O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0644); if (out_fd < 0) fatal_error(errno, "Failed to open \"%" TS"s\"", out_filename); /* Create a compressor for the compression type and chunk size with the * default parameters. */ ret = wimlib_create_compressor(ctype, chunk_size, 0, &compressor); if (ret != 0) fatal_error(0, "Failed to create compressor: %" TS, wimlib_get_error_string((enum wimlib_error_code)ret)); ctype32 = (uint32_t)ctype; /* Write compression type and chunk size to the file. */ if (write(out_fd, &ctype32, sizeof(uint32_t)) != sizeof(uint32_t) || write(out_fd, &chunk_size, sizeof(uint32_t)) != sizeof(uint32_t)) { fatal_error(errno, "Error writing to \"%" TS"\"", out_filename); } /* Compress and write the data. */ do_compress(in_fd, in_filename, out_fd, out_filename, chunk_size, compressor); /* Cleanup and return. */ if (close(out_fd)) fatal_error(errno, "Error closing \"%" TS"\"", out_filename); wimlib_free_compressor(compressor); return 0; } wimlib-1.13.1/examples/Makefile0000644000175000017500000000022713016523676013271 00000000000000CFLAGS := -Wall LDLIBS := -lwim EXE := applywim capturewim updatewim compressfile decompressfile all:$(EXE) clean: rm -f $(EXE) .PHONY: all clean wimlib-1.13.1/COPYING.GPLv30000644000175000017500000010451313016523676011743 00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . wimlib-1.13.1/INSTALL0000644000175000017500000003660512260745400011044 00000000000000Installation Instructions ************************* Copyright (C) 1994-1996, 1999-2002, 2004-2012 Free Software Foundation, Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without warranty of any kind. Basic Installation ================== Briefly, the shell commands `./configure; make; make install' should configure, build, and install this package. The following more-detailed instructions are generic; see the `README' file for instructions specific to this package. Some packages provide this `INSTALL' file but do not implement all of the features documented below. The lack of an optional feature in a given package is not necessarily a bug. More recommendations for GNU packages can be found in *note Makefile Conventions: (standards)Makefile Conventions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. Caching is disabled by default to prevent problems with accidental use of stale cache files. If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. Running `configure' might take a while. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package, generally using the just-built uninstalled binaries. 4. Type `make install' to install the programs and any data files and documentation. When installing into a prefix owned by root, it is recommended that the package be configured and built as a regular user, and only the `make install' phase executed with root privileges. 5. Optionally, type `make installcheck' to repeat any self-tests, but this time using the binaries in their final installed location. This target does not install anything. Running this target as a regular user, particularly if the prior `make install' required root privileges, verifies that the installation completed correctly. 6. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. 7. Often, you can also type `make uninstall' to remove the installed files again. In practice, not all packages have tested that uninstallation works correctly, even though it is required by the GNU Coding Standards. 8. Some packages, particularly those that use Automake, provide `make distcheck', which can by used by developers to test that all other targets like `make install' and `make uninstall' work correctly. This target is generally not run by end users. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c99 CFLAGS=-g LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you can use GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. This is known as a "VPATH" build. With a non-GNU `make', it is safer to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. On MacOS X 10.5 and later systems, you can create libraries and executables that work on multiple system types--known as "fat" or "universal" binaries--by specifying multiple `-arch' options to the compiler but only a single `-arch' option to the preprocessor. Like this: ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CPP="gcc -E" CXXCPP="g++ -E" This is not guaranteed to produce working output in all cases, you may have to build one architecture at a time and combine the results using the `lipo' tool if you have problems. Installation Names ================== By default, `make install' installs the package's commands under `/usr/local/bin', include files under `/usr/local/include', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PREFIX', where PREFIX must be an absolute file name. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option `--exec-prefix=PREFIX' to `configure', the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. In general, the default for these options is expressed in terms of `${prefix}', so that specifying just `--prefix' will affect all of the other directory specifications that were not explicitly provided. The most portable way to affect installation locations is to pass the correct locations to `configure'; however, many packages provide one or both of the following shortcuts of passing variable assignments to the `make install' command line to change installation locations without having to reconfigure or recompile. The first method involves providing an override variable for each affected directory. For example, `make install prefix=/alternate/directory' will choose an alternate location for all directory configuration variables that were expressed in terms of `${prefix}'. Any directories that were specified during `configure', but not in terms of `${prefix}', must each be overridden at install time for the entire installation to be relocated. The approach of makefile variable overrides for each directory variable is required by the GNU Coding Standards, and ideally causes no recompilation. However, some platforms have known limitations with the semantics of shared libraries that end up requiring recompilation when using this method, particularly noticeable in packages that use GNU Libtool. The second method involves providing the `DESTDIR' variable. For example, `make install DESTDIR=/alternate/directory' will prepend `/alternate/directory' before all installation names. The approach of `DESTDIR' overrides is not required by the GNU Coding Standards, and does not work on platforms that have drive letters. On the other hand, it does better at avoiding recompilation issues, and works well even when some directory options were not specified in terms of `${prefix}' at `configure' time. Optional Features ================= If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Some packages offer the ability to configure how verbose the execution of `make' will be. For these packages, running `./configure --enable-silent-rules' sets the default to minimal output, which can be overridden with `make V=1'; while running `./configure --disable-silent-rules' sets the default to verbose, which can be overridden with `make V=0'. Particular systems ================== On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC is not installed, it is recommended to use the following options in order to use an ANSI C compiler: ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" and if that doesn't work, install pre-built binaries of GCC for HP-UX. HP-UX `make' updates targets which have the same time stamps as their prerequisites, which makes it generally unusable when shipped generated files such as `configure' are involved. Use GNU `make' instead. On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot parse its `' header file. The option `-nodtk' can be used as a workaround. If GNU CC is not installed, it is therefore recommended to try ./configure CC="cc" and if that doesn't work, try ./configure CC="cc -nodtk" On Solaris, don't put `/usr/ucb' early in your `PATH'. This directory contains several dysfunctional programs; working variants of these programs are available in `/usr/bin'. So, if you need `/usr/ucb' in your `PATH', put it _after_ `/usr/bin'. On Haiku, software installed for all users goes in `/boot/common', not `/usr/local'. It is recommended to use the following options: ./configure --prefix=/boot/common Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the option `--target=TYPE' to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for `CONFIG_SHELL' due to an Autoconf limitation. Until the limitation is lifted, you can use this workaround: CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of all of the options to `configure', and exit. `--help=short' `--help=recursive' Print a summary of the options unique to this package's `configure', and exit. The `short' variant lists options used only in the top level, while the `recursive' variant lists options also present in any nested packages. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `--prefix=DIR' Use DIR as the installation prefix. *note Installation Names:: for more details, including other options available for fine-tuning the installation locations. `--no-create' `-n' Run the configure checks, but stop before creating any output files. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. wimlib-1.13.1/COPYING.LGPLv30000644000175000017500000001672513016523676012066 00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. wimlib-1.13.1/build-aux/0000755000175000017500000000000013464166632011766 500000000000000wimlib-1.13.1/build-aux/depcomp0000744000175000017500000005602013427652377013271 00000000000000#! /bin/sh # depcomp - compile a program generating dependencies as side-effects scriptversion=2018-03-07.03; # UTC # Copyright (C) 1999-2018 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 Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Alexandre Oliva . case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] Run PROGRAMS ARGS to compile a file, generating dependencies as side-effects. Environment variables: depmode Dependency tracking mode. source Source file read by 'PROGRAMS ARGS'. object Object file output by 'PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputting dependencies. libtool Whether libtool is used (yes/no). Report bugs to . EOF exit $? ;; -v | --v*) echo "depcomp $scriptversion" exit $? ;; esac # Get the directory component of the given path, and save it in the # global variables '$dir'. Note that this directory component will # be either empty or ending with a '/' character. This is deliberate. set_dir_from () { case $1 in */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; *) dir=;; esac } # Get the suffix-stripped basename of the given path, and save it the # global variable '$base'. set_base_from () { base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` } # If no dependency file was actually created by the compiler invocation, # we still have to create a dummy depfile, to avoid errors with the # Makefile "include basename.Plo" scheme. make_dummy_depfile () { echo "#dummy" > "$depfile" } # Factor out some common post-processing of the generated depfile. # Requires the auxiliary global variable '$tmpdepfile' to be set. aix_post_process_depfile () { # If the compiler actually managed to produce a dependency file, # post-process it. if test -f "$tmpdepfile"; then # Each line is of the form 'foo.o: dependency.h'. # Do two passes, one to just change these to # $object: dependency.h # and one to simply output # dependency.h: # which is needed to avoid the deleted-header problem. { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" } > "$depfile" rm -f "$tmpdepfile" else make_dummy_depfile fi } # A tabulation character. tab=' ' # A newline character. nl=' ' # Character ranges might be problematic outside the C locale. # These definitions help. upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ lower=abcdefghijklmnopqrstuvwxyz digits=0123456789 alpha=${upper}${lower} if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 fi # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. depfile=${depfile-`echo "$object" | sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" # Avoid interferences from the environment. gccflag= dashmflag= # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case # here, because this file can only contain one case statement. if test "$depmode" = hp; then # HP compiler uses -M and no extra arg. gccflag=-M depmode=gcc fi if test "$depmode" = dashXmstdout; then # This is just like dashmstdout with a different argument. dashmflag=-xM depmode=dashmstdout fi cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then # This is just like msvisualcpp but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvisualcpp fi if test "$depmode" = msvc7msys; then # This is just like msvc7 but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvc7 fi if test "$depmode" = xlc; then # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. gccflag=-qmakedep=gcc,-MF depmode=gcc fi case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what ## we want. Yay! Note: for some reason libtool 1.4 doesn't like ## it if -MD -MP comes after the -MF stuff. Hmm. ## Unfortunately, FreeBSD c89 acceptance of flags depends upon ## the command line argument order; so add the flags where they ## appear in depend2.am. Note that the slowdown incurred here ## affects only configure: in makefiles, %FASTDEP% shortcuts this. for arg do case $arg in -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; *) set fnord "$@" "$arg" ;; esac shift # fnord shift # $arg done "$@" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. ## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. ## (see the conditional assignment to $gccflag above). ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like ## -MM, not -M (despite what the docs say). Also, it might not be ## supported by the other compilers which use the 'gcc' depmode. ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then gccflag=-MD, fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The second -e expression handles DOS-style file names with drive # letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" ## This next piece of magic avoids the "deleted header file" problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. ## Some versions of gcc put a space before the ':'. On the theory ## that the space means something, we add a space to the output as ## well. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; sgi) if test "$libtool" = yes; then "$@" "-Wp,-MDupdate,$tmpdepfile" else "$@" -MDupdate "$tmpdepfile" fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; # the IRIX cc adds comments like '#:fec' to the end of the # dependency line. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ | tr "$nl" ' ' >> "$depfile" echo >> "$depfile" # The second pass generates a dummy entry for each header file. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" ;; xlc) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the # current directory. Also, the AIX compiler puts '$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u tmpdepfile3=$dir.libs/$base.u "$@" -Wc,-M else tmpdepfile1=$dir$base.u tmpdepfile2=$dir$base.u tmpdepfile3=$dir$base.u "$@" -M fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done aix_post_process_depfile ;; tcc) # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 # FIXME: That version still under development at the moment of writing. # Make that this statement remains true also for stable, released # versions. # It will wrap lines (doesn't matter whether long or short) with a # trailing '\', as in: # # foo.o : \ # foo.c \ # foo.h \ # # It will put a trailing '\' even on the last line, and will use leading # spaces rather than leading tabs (at least since its commit 0394caf7 # "Emit spaces for -MD"). "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. # We have to change lines of the first kind to '$object: \'. sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" # And for each line of the second kind, we have to emit a 'dep.h:' # dummy dependency, to avoid the deleted-header problem. sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" rm -f "$tmpdepfile" ;; ## The order of this option in the case statement is important, since the ## shell code in configure will try each of these formats in the order ## listed in this file. A plain '-MD' option would be understood by many ## compilers, so we must ensure this comes after the gcc and icc options. pgcc) # Portland's C compiler understands '-MD'. # Will always output deps to 'file.d' where file is the root name of the # source file under compilation, even if file resides in a subdirectory. # The object file name does not affect the name of the '.d' file. # pgcc 10.2 will output # foo.o: sub/foo.c sub/foo.h # and will wrap long lines using '\' : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... set_dir_from "$object" # Use the source, not the object, to determine the base name, since # that's sadly what pgcc will do too. set_base_from "$source" tmpdepfile=$base.d # For projects that build the same source file twice into different object # files, the pgcc approach of using the *source* file root name can cause # problems in parallel builds. Use a locking strategy to avoid stomping on # the same $tmpdepfile. lockdir=$base.d-lock trap " echo '$0: caught signal, cleaning up...' >&2 rmdir '$lockdir' exit 1 " 1 2 13 15 numtries=100 i=$numtries while test $i -gt 0; do # mkdir is a portable test-and-set. if mkdir "$lockdir" 2>/dev/null; then # This process acquired the lock. "$@" -MD stat=$? # Release the lock. rmdir "$lockdir" break else # If the lock is being held by a different process, wait # until the winning process is done or we timeout. while test -d "$lockdir" && test $i -gt 0; do sleep 1 i=`expr $i - 1` done fi i=`expr $i - 1` done trap - 1 2 13 15 if test $i -le 0; then echo "$0: failed to acquire lock after $numtries attempts" >&2 echo "$0: check lockdir '$lockdir'" >&2 exit 1 fi if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each line is of the form `foo.o: dependent.h', # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp2) # The "hp" stanza above does not work with aCC (C++) and HP's ia64 # compilers, which have integrated preprocessors. The correct option # to use with these is +Maked; it writes dependencies to a file named # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d "$@" -Wc,+Maked else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d "$@" +Maked fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" # Add 'dependent.h:' lines. sed -ne '2,${ s/^ *// s/ \\*$// s/$/:/ p }' "$tmpdepfile" >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; tru64) # The Tru64 compiler uses -MD to generate dependencies as a side # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put # dependencies in 'foo.d' instead, so we check for that too. # Subdirectories are respected. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then # Libtool generates 2 separate objects for the 2 libraries. These # two compilations output dependencies in $dir.libs/$base.o.d and # in $dir$base.o.d. We have to check for both files, because # one of the two compilations can be disabled. We should prefer # $dir$base.o.d over $dir.libs/$base.o.d because the latter is # automatically cleaned when .libs/ is deleted, while ignoring # the former would cause a distcleancheck panic. tmpdepfile1=$dir$base.o.d # libtool 1.5 tmpdepfile2=$dir.libs/$base.o.d # Likewise. tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 "$@" -Wc,-MD else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d tmpdepfile3=$dir$base.d "$@" -MD fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done # Same post-processing that is required for AIX mode. aix_post_process_depfile ;; msvc7) if test "$libtool" = yes; then showIncludes=-Wc,-showIncludes else showIncludes=-showIncludes fi "$@" $showIncludes > "$tmpdepfile" stat=$? grep -v '^Note: including file: ' "$tmpdepfile" if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The first sed program below extracts the file names and escapes # backslashes for cygpath. The second sed program outputs the file # name when reading, but also accumulates all include files in the # hold buffer in order to output them again at the end. This only # works with sed implementations that can handle large buffers. sed < "$tmpdepfile" -n ' /^Note: including file: *\(.*\)/ { s//\1/ s/\\/\\\\/g p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g s/\(.*\)/'"$tab"'\1 \\/p s/.\(.*\) \\/\1:/ H $ { s/.*/'"$tab"'/ G p }' >> "$depfile" echo >> "$depfile" # make sure the fragment doesn't end with a backslash rm -f "$tmpdepfile" ;; msvc7msys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; #nosideeffect) # This comment above is used by automake to tell side-effect # dependency tracking mechanisms from slower ones. dashmstdout) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout, regardless of -o. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done test -z "$dashmflag" && dashmflag=-M # Require at least two characters before searching for ':' # in the target name. This is to cope with DOS-style filenames: # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. "$@" $dashmflag | sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this sed invocation # correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; dashXmstdout) # This case only exists to satisfy depend.m4. It is never actually # run, as this mode is specially recognized in the preamble. exit 1 ;; makedepend) "$@" || exit $? # Remove any Libtool call if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # X makedepend shift cleared=no eat=no for arg do case $cleared in no) set ""; shift cleared=yes ;; esac if test $eat = yes; then eat=no continue fi case "$arg" in -D*|-I*) set fnord "$@" "$arg"; shift ;; # Strip any option that makedepend may not understand. Remove # the object too, otherwise makedepend will parse it as a source file. -arch) eat=yes ;; -*|$object) ;; *) set fnord "$@" "$arg"; shift ;; esac done obj_suffix=`echo "$object" | sed 's/^.*\././'` touch "$tmpdepfile" ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" rm -f "$depfile" # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process the last invocation # correctly. Breaking it into two sed invocations is a workaround. sed '1,2d' "$tmpdepfile" \ | tr ' ' "$nl" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; cpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done "$@" -E \ | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; msvisualcpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi IFS=" " for arg do case "$arg" in -o) shift ;; $object) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") set fnord "$@" shift shift ;; *) set fnord "$@" "$arg" shift shift ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" echo "$tab" >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; msvcmsys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; none) exec "$@" ;; *) echo "Unknown depmode $depmode" 1>&2 exit 1 ;; esac exit 0 # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: wimlib-1.13.1/build-aux/config.guess0000744000175000017500000012620613427652377014240 00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2018 Free Software Foundation, Inc. timestamp='2018-03-08' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess # # Please send patches to . me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > "$dummy.c" ; for c in cc gcc c89 c99 ; do if ($c -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case "$UNAME_SYSTEM" in Linux|GNU|GNU/*) # If the system lacks a compiler, then just pick glibc. # We could probably try harder. LIBC=gnu eval "$set_cc_for_build" cat <<-EOF > "$dummy.c" #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #else LIBC=gnu #endif EOF eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`" # If ldd exists, use it to detect musl libc. if command -v ldd >/dev/null && \ ldd --version 2>&1 | grep -q ^musl then LIBC=musl fi ;; esac # Note: order is significant - the case branches are not exclusive. case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ "/sbin/$sysctl" 2>/dev/null || \ "/usr/sbin/$sysctl" 2>/dev/null || \ echo unknown)` case "$UNAME_MACHINE_ARCH" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` machine="${arch}${endian}"-unknown ;; *) machine="$UNAME_MACHINE_ARCH"-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently (or will in the future) and ABI. case "$UNAME_MACHINE_ARCH" in earm*) os=netbsdelf ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval "$set_cc_for_build" if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # Determine ABI tags. case "$UNAME_MACHINE_ARCH" in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "$UNAME_VERSION" in Debian*) release='-gnu' ;; *) release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "$machine-${os}${release}${abi}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE" exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE" exit ;; *:LibertyBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE" exit ;; *:MidnightBSD:*:*) echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE" exit ;; *:ekkoBSD:*:*) echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE" exit ;; *:SolidBSD:*:*) echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE" exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd"$UNAME_RELEASE" exit ;; *:MirBSD:*:*) echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE" exit ;; *:Sortix:*:*) echo "$UNAME_MACHINE"-unknown-sortix exit ;; *:Redox:*:*) echo "$UNAME_MACHINE"-unknown-redox exit ;; mips:OSF1:*.*) echo mips-dec-osf1 exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE=alpha ;; "EV4.5 (21064)") UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") UNAME_MACHINE=alpha ;; "EV5 (21164)") UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`" # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo "$UNAME_MACHINE"-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo "$UNAME_MACHINE"-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix"$UNAME_RELEASE" exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux"$UNAME_RELEASE" exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval "$set_cc_for_build" SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH=x86_64 fi fi echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`" exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos"$UNAME_RELEASE" exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos"$UNAME_RELEASE" ;; sun4) echo sparc-sun-sunos"$UNAME_RELEASE" ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos"$UNAME_RELEASE" exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint"$UNAME_RELEASE" exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint"$UNAME_RELEASE" exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint"$UNAME_RELEASE" exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint"$UNAME_RELEASE" exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint"$UNAME_RELEASE" exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint"$UNAME_RELEASE" exit ;; m68k:machten:*:*) echo m68k-apple-machten"$UNAME_RELEASE" exit ;; powerpc:machten:*:*) echo powerpc-apple-machten"$UNAME_RELEASE" exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix"$UNAME_RELEASE" exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix"$UNAME_RELEASE" exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix"$UNAME_RELEASE" exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`"$dummy" "$dummyarg"` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos"$UNAME_RELEASE" exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ] then if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \ [ "$TARGET_BINARY_INTERFACE"x = x ] then echo m88k-dg-dgux"$UNAME_RELEASE" else echo m88k-dg-dguxbcs"$UNAME_RELEASE" fi else echo i586-dg-dgux"$UNAME_RELEASE" fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`" exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV" exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/lslpp ] ; then IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi echo "$IBM_ARCH"-ibm-aix"$IBM_REV" exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` case "$UNAME_MACHINE" in 9000/31?) HP_ARCH=m68000 ;; 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "$sc_cpu_version" in 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "$sc_kernel_bits" in 32) HP_ARCH=hppa2.0n ;; 64) HP_ARCH=hppa2.0w ;; '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi if [ "$HP_ARCH" = "" ]; then eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ "$HP_ARCH" = hppa2.0w ] then eval "$set_cc_for_build" # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH=hppa2.0w else HP_ARCH=hppa64 fi fi echo "$HP_ARCH"-hp-hpux"$HPUX_REV" exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux"$HPUX_REV" exit ;; 3050*:HI-UX:*:*) eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo "$UNAME_MACHINE"-unknown-osf1mk else echo "$UNAME_MACHINE"-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE" exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi"$UNAME_RELEASE" exit ;; *:BSD/OS:*:*) echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE" exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case "$UNAME_PROCESSOR" in amd64) UNAME_PROCESSOR=x86_64 ;; i386) UNAME_PROCESSOR=i586 ;; esac echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" exit ;; i*:CYGWIN*:*) echo "$UNAME_MACHINE"-pc-cygwin exit ;; *:MINGW64*:*) echo "$UNAME_MACHINE"-pc-mingw64 exit ;; *:MINGW*:*) echo "$UNAME_MACHINE"-pc-mingw32 exit ;; *:MSYS*:*) echo "$UNAME_MACHINE"-pc-msys exit ;; i*:PW*:*) echo "$UNAME_MACHINE"-pc-pw32 exit ;; *:Interix*:*) case "$UNAME_MACHINE" in x86) echo i586-pc-interix"$UNAME_RELEASE" exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix"$UNAME_RELEASE" exit ;; IA64) echo ia64-unknown-interix"$UNAME_RELEASE" exit ;; esac ;; i*:UWIN*:*) echo "$UNAME_MACHINE"-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; *:GNU:*:*) # the GNU system echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`" exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC" exit ;; i*86:Minix:*:*) echo "$UNAME_MACHINE"-pc-minix exit ;; aarch64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC=gnulibc1 ; fi echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; arc:Linux:*:* | arceb:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; arm*:Linux:*:*) eval "$set_cc_for_build" if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi else echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf fi fi exit ;; avr32*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; cris:Linux:*:*) echo "$UNAME_MACHINE"-axis-linux-"$LIBC" exit ;; crisv32:Linux:*:*) echo "$UNAME_MACHINE"-axis-linux-"$LIBC" exit ;; e2k:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; frv:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; hexagon:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; i*86:Linux:*:*) echo "$UNAME_MACHINE"-pc-linux-"$LIBC" exit ;; ia64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; k1om:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; m32r*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; m68*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU'`" test "x$CPU" != x && { echo "$CPU-unknown-linux-$LIBC"; exit; } ;; mips64el:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; openrisc*:Linux:*:*) echo or1k-unknown-linux-"$LIBC" exit ;; or32:Linux:*:* | or1k*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; padre:Linux:*:*) echo sparc-unknown-linux-"$LIBC" exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-"$LIBC" exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;; PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;; *) echo hppa-unknown-linux-"$LIBC" ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-"$LIBC" exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-"$LIBC" exit ;; ppc64le:Linux:*:*) echo powerpc64le-unknown-linux-"$LIBC" exit ;; ppcle:Linux:*:*) echo powerpcle-unknown-linux-"$LIBC" exit ;; riscv32:Linux:*:* | riscv64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo "$UNAME_MACHINE"-ibm-linux-"$LIBC" exit ;; sh64*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; sh*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; tile*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; vax:Linux:*:*) echo "$UNAME_MACHINE"-dec-linux-"$LIBC" exit ;; x86_64:Linux:*:*) echo "$UNAME_MACHINE"-pc-linux-"$LIBC" exit ;; xtensa*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION" exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo "$UNAME_MACHINE"-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo "$UNAME_MACHINE"-unknown-stop exit ;; i*86:atheos:*:*) echo "$UNAME_MACHINE"-unknown-atheos exit ;; i*86:syllable:*:*) echo "$UNAME_MACHINE"-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos"$UNAME_RELEASE" exit ;; i*86:*DOS:*:*) echo "$UNAME_MACHINE"-pc-msdosdjgpp exit ;; i*86:*:4.*:*) UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL" else echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL" fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}{$UNAME_VERSION}" exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL" else echo "$UNAME_MACHINE"-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos"$UNAME_RELEASE" exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos"$UNAME_RELEASE" exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos"$UNAME_RELEASE" exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos"$UNAME_RELEASE" exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv"$UNAME_RELEASE" exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo "$UNAME_MACHINE"-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo "$UNAME_MACHINE"-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux"$UNAME_RELEASE" exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv"$UNAME_RELEASE" else echo mips-unknown-sysv"$UNAME_RELEASE" fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; x86_64:Haiku:*:*) echo x86_64-unknown-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux"$UNAME_RELEASE" exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux"$UNAME_RELEASE" exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux"$UNAME_RELEASE" exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux"$UNAME_RELEASE" exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux"$UNAME_RELEASE" exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux"$UNAME_RELEASE" exit ;; SX-ACE:SUPER-UX:*:*) echo sxace-nec-superux"$UNAME_RELEASE" exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody"$UNAME_RELEASE" exit ;; *:Rhapsody:*:*) echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE" exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown eval "$set_cc_for_build" if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi if test "`echo "$UNAME_RELEASE" | sed -e 's/\..*//'`" -le 10 ; then if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_PPC >/dev/null then UNAME_PROCESSOR=powerpc fi fi elif test "$UNAME_PROCESSOR" = i386 ; then # Avoid executing cc on OS X 10.9, as it ships with a stub # that puts up a graphical alert prompting to install # developer tools. Any system running Mac OS X 10.7 or # later (Darwin 11 and later) is required to have a 64-bit # processor. This is not true of the ARM version of Darwin # that Apple uses in portable devices. UNAME_PROCESSOR=x86_64 fi echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE" exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE" exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-*:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk"$UNAME_RELEASE" exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk"$UNAME_RELEASE" exit ;; NSR-*:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk"$UNAME_RELEASE" exit ;; NSV-*:NONSTOP_KERNEL:*:*) echo nsv-tandem-nsk"$UNAME_RELEASE" exit ;; NSX-*:NONSTOP_KERNEL:*:*) echo nsx-tandem-nsk"$UNAME_RELEASE" exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE" exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = 386; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo "$UNAME_MACHINE"-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux"$UNAME_RELEASE" exit ;; *:DragonFly:*:*) echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "$UNAME_MACHINE" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`" exit ;; i*86:rdos:*:*) echo "$UNAME_MACHINE"-pc-rdos exit ;; i*86:AROS:*:*) echo "$UNAME_MACHINE"-pc-aros exit ;; x86_64:VMkernel:*:*) echo "$UNAME_MACHINE"-unknown-esx exit ;; amd64:Isilon\ OneFS:*:*) echo x86_64-unknown-onefs exit ;; esac echo "$0: unable to guess system type" >&2 case "$UNAME_MACHINE:$UNAME_SYSTEM" in mips:Linux | mips64:Linux) # If we got here on MIPS GNU/Linux, output extra information. cat >&2 <&2 </dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = "$UNAME_MACHINE" UNAME_RELEASE = "$UNAME_RELEASE" UNAME_SYSTEM = "$UNAME_SYSTEM" UNAME_VERSION = "$UNAME_VERSION" EOF exit 1 # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: wimlib-1.13.1/build-aux/config.sub0000744000175000017500000010645513427652377013707 00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2018 Free Software Foundation, Inc. timestamp='2018-03-08' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo "$1" exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ kopensolaris*-gnu* | cloudabi*-eabi* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; android-linux) os=-linux-android basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown ;; *) basic_machine=`echo "$1" | sed 's/-[^-]*$//'` if [ "$basic_machine" != "$1" ] then os=`echo "$1" | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray | -microblaze*) os= basic_machine=$1 ;; -bluegene*) os=-cnk ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco6) os=-sco5v6 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco5) os=-sco3.2v5 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -lynx*178) os=-lynxos178 ;; -lynx*5) os=-lynxos5 ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo "$1" | sed -e 's/86-.*/86-sequent/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | aarch64 | aarch64_be \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arceb \ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ | avr | avr32 \ | ba \ | be32 | be64 \ | bfin \ | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ | e2k | epiphany \ | fido | fr30 | frv | ft32 \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia16 | ia64 \ | ip2k | iq2000 \ | k1om \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa32r6 | mipsisa32r6el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64r6 | mipsisa64r6el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipsr5900 | mipsr5900el \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ | open8 | or1k | or1knd | or32 \ | pdp10 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pru \ | pyramid \ | riscv32 | riscv64 \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu \ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | visium \ | wasm32 \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; c54x) basic_machine=tic54x-unknown ;; c55x) basic_machine=tic55x-unknown ;; c6x) basic_machine=tic6x-unknown ;; leon|leon[3-9]) basic_machine=sparc-$basic_machine ;; m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65) ;; ms1) basic_machine=mt-unknown ;; strongarm | thumb | xscale) basic_machine=arm-unknown ;; xgate) basic_machine=$basic_machine-unknown os=-none ;; xscaleeb) basic_machine=armeb-unknown ;; xscaleel) basic_machine=armel-unknown ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | ba-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | e2k-* | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia16-* | ia64-* \ | ip2k-* | iq2000-* \ | k1om-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ | microblaze-* | microblazeel-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa32r6-* | mipsisa32r6el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64r6-* | mipsisa64r6el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipsr5900-* | mipsr5900el-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ | or1k*-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pru-* \ | pyramid-* \ | riscv32-* | riscv64-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ | tron-* \ | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | visium-* \ | wasm32-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-pc os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aros) basic_machine=i386-pc os=-aros ;; asmjs) basic_machine=asmjs-unknown ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; blackfin) basic_machine=bfin-unknown os=-linux ;; blackfin-*) basic_machine=bfin-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) basic_machine=powerpc-ibm os=-cnk ;; c54x-*) basic_machine=tic54x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c55x-*) basic_machine=tic55x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c6x-*) basic_machine=tic6x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray os=-unicos ;; cegcc) basic_machine=arm-unknown os=-cegcc ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16 | cr16-*) basic_machine=cr16-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; dicos) basic_machine=i686-pc os=-dicos ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2*) basic_machine=m68k-bull os=-sysv3 ;; e500v[12]) basic_machine=powerpc-unknown os=$os"spe" ;; e500v[12]-*) basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=$os"spe" ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; i*86v32) basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; leon-*|leon[3-9]-*) basic_machine=sparc-`echo "$basic_machine" | sed 's/-.*//'` ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) basic_machine=m68k-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=-linux ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; microblaze*) basic_machine=microblaze-xilinx ;; mingw64) basic_machine=x86_64-pc os=-mingw64 ;; mingw32) basic_machine=i686-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; moxiebox) basic_machine=moxie-unknown os=-moxiebox ;; msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo "$basic_machine" | sed -e 's/ms1-/mt-/'` ;; msys) basic_machine=i686-pc os=-msys ;; mvs) basic_machine=i370-ibm os=-mvs ;; nacl) basic_machine=le32-unknown os=-nacl ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; neo-tandem) basic_machine=neo-tandem ;; nse-tandem) basic_machine=nse-tandem ;; nsr-tandem) basic_machine=nsr-tandem ;; nsv-tandem) basic_machine=nsv-tandem ;; nsx-tandem) basic_machine=nsx-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; parisc) basic_machine=hppa-unknown os=-linux ;; parisc-*) basic_machine=hppa-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=-linux ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rdos | rdos64) basic_machine=x86_64-pc os=-rdos ;; rdos32) basic_machine=i386-pc os=-rdos ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh5el) basic_machine=sh5le-unknown ;; simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; strongarm-* | thumb-*) basic_machine=arm-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tile*) basic_machine=$basic_machine-unknown os=-linux-gnu ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; x64) basic_machine=x86_64-pc ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) basic_machine=`echo "$basic_machine" | sed 's/^xscale/arm/'` ;; ymp) basic_machine=ymp-cray os=-unicos ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo "$basic_machine" | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo "$basic_machine" | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases that might get confused # with valid system types. # -solaris* is a basic system type, with this one exception. -auroraux) os=-auroraux ;; -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # es1800 is here to avoid being matched by es* (a different OS) -es1800*) os=-ose ;; # Now accept the basic system types. # The portable systems comes first. # Each alternative MUST end in a * to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* | -plan9* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* | -cloudabi* | -sortix* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -knetbsd* | -mirbsd* | -netbsd* \ | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* | -hcos* \ | -chorusos* | -chorusrdb* | -cegcc* | -glidix* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \ | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox* | -bme* \ | -midnightbsd*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -xray | -os68k* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo "$os" | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo "$os" | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo "$os" | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4*) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -pikeos*) # Until real need of OS specific support for # particular features comes up, bare metal # configurations are quite functional. case $basic_machine in arm*) os=-eabi ;; *) os=-elf ;; esac ;; -nacl*) ;; -ios) ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; c8051-*) os=-elf ;; hexagon-*) os=-elf ;; tic54x-*) os=-coff ;; tic55x-*) os=-coff ;; tic6x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; pru-*) os=-elf ;; *-be) os=-beos ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -cnk*|-aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo "$basic_machine" | sed "s/unknown/$vendor/"` ;; esac echo "$basic_machine$os" exit # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: wimlib-1.13.1/build-aux/config.rpath0000644000175000017500000000000013427652371014200 00000000000000wimlib-1.13.1/build-aux/ltmain.sh0000644000175000017500000120671413427652374013543 00000000000000#! /bin/sh ## DO NOT EDIT - This file generated from ./build-aux/ltmain.in ## by inline-source v2018-07-24.06 # libtool (GNU libtool) 2.4.6.42-b88ce # Provide generalized library-building support services. # Written by Gordon Matzigkeit , 1996 # Copyright (C) 1996-2018 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . PROGRAM=libtool PACKAGE=libtool VERSION=2.4.6.42-b88ce package_revision=2.4.6.42 ## ------ ## ## Usage. ## ## ------ ## # Run './libtool --help' for help with using this script from the # command line. ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # After configure completes, it has a better idea of some of the # shell tools we need than the defaults used by the functions shared # with bootstrap, so set those here where they can still be over- # ridden by the user, but otherwise take precedence. : ${AUTOCONF="autoconf"} : ${AUTOMAKE="automake"} ## -------------------------- ## ## Source external libraries. ## ## -------------------------- ## # Much of our low-level functionality needs to be sourced from external # libraries, which are installed to $pkgauxdir. # Set a version string for this script. scriptversion=2018-07-24.06; # UTC # General shell script boiler plate, and helper functions. # Written by Gary V. Vaughan, 2004 # This is free software. There is NO warranty; not even for # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Copyright (C) 2004-2018 Bootstrap Authors # # This file is dual licensed under the terms of the MIT license # , and GPL version 3 or later # . You must apply one of # these licenses when using or redistributing this software or any of # the files within it. See the URLs above, or the file `LICENSE` # included in the Bootstrap distribution for the full license texts. # Please report bugs or propose patches to: # ## ------ ## ## Usage. ## ## ------ ## # Evaluate this file near the top of your script to gain access to # the functions and variables defined here: # # . `echo "$0" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh # # If you need to override any of the default environment variable # settings, do that before evaluating this file. ## -------------------- ## ## Shell normalisation. ## ## -------------------- ## # Some shells need a little help to be as Bourne compatible as possible. # Before doing anything else, make sure all that help has been provided! DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi # NLS nuisances: We save the old values in case they are required later. _G_user_locale= _G_safe_locale= for _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test set = \"\${$_G_var+set}\"; then save_$_G_var=\$$_G_var $_G_var=C export $_G_var _G_user_locale=\"$_G_var=\\\$save_\$_G_var; \$_G_user_locale\" _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\" fi" done # Make sure IFS has a sensible default sp=' ' nl=' ' IFS="$sp $nl" # There are apparently some retarded systems that use ';' as a PATH separator! if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # func_unset VAR # -------------- # Portably unset VAR. # In some shells, an 'unset VAR' statement leaves a non-zero return # status if VAR is already unset, which might be problematic if the # statement is used at the end of a function (thus poisoning its return # value) or when 'set -e' is active (causing even a spurious abort of # the script in this case). func_unset () { { eval $1=; (eval unset $1) >/dev/null 2>&1 && eval unset $1 || : ; } } # Make sure CDPATH doesn't cause `cd` commands to output the target dir. func_unset CDPATH # Make sure ${,E,F}GREP behave sanely. func_unset GREP_OPTIONS ## ------------------------- ## ## Locate command utilities. ## ## ------------------------- ## # func_executable_p FILE # ---------------------- # Check that FILE is an executable regular file. func_executable_p () { test -f "$1" && test -x "$1" } # func_path_progs PROGS_LIST CHECK_FUNC [PATH] # -------------------------------------------- # Search for either a program that responds to --version with output # containing "GNU", or else returned by CHECK_FUNC otherwise, by # trying all the directories in PATH with each of the elements of # PROGS_LIST. # # CHECK_FUNC should accept the path to a candidate program, and # set $func_check_prog_result if it truncates its output less than # $_G_path_prog_max characters. func_path_progs () { _G_progs_list=$1 _G_check_func=$2 _G_PATH=${3-"$PATH"} _G_path_prog_max=0 _G_path_prog_found=false _G_save_IFS=$IFS; IFS=${PATH_SEPARATOR-:} for _G_dir in $_G_PATH; do IFS=$_G_save_IFS test -z "$_G_dir" && _G_dir=. for _G_prog_name in $_G_progs_list; do for _exeext in '' .EXE; do _G_path_prog=$_G_dir/$_G_prog_name$_exeext func_executable_p "$_G_path_prog" || continue case `"$_G_path_prog" --version 2>&1` in *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;; *) $_G_check_func $_G_path_prog func_path_progs_result=$func_check_prog_result ;; esac $_G_path_prog_found && break 3 done done done IFS=$_G_save_IFS test -z "$func_path_progs_result" && { echo "no acceptable sed could be found in \$PATH" >&2 exit 1 } } # We want to be able to use the functions in this file before configure # has figured out where the best binaries are kept, which means we have # to search for them ourselves - except when the results are already set # where we skip the searches. # Unless the user overrides by setting SED, search the path for either GNU # sed, or the sed that truncates its output the least. test -z "$SED" && { _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for _G_i in 1 2 3 4 5 6 7; do _G_sed_script=$_G_sed_script$nl$_G_sed_script done echo "$_G_sed_script" 2>/dev/null | sed 99q >conftest.sed _G_sed_script= func_check_prog_sed () { _G_path_prog=$1 _G_count=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo '' >> conftest.nl "$_G_path_prog" -f conftest.sed conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "sed gsed" func_check_prog_sed "$PATH:/usr/xpg4/bin" rm -f conftest.sed SED=$func_path_progs_result } # Unless the user overrides by setting GREP, search the path for either GNU # grep, or the grep that truncates its output the least. test -z "$GREP" && { func_check_prog_grep () { _G_path_prog=$1 _G_count=0 _G_path_prog_max=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo 'GREP' >> conftest.nl "$_G_path_prog" -e 'GREP$' -e '-(cannot match)-' conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "grep ggrep" func_check_prog_grep "$PATH:/usr/xpg4/bin" GREP=$func_path_progs_result } ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # All uppercase variable names are used for environment variables. These # variables can be overridden by the user before calling a script that # uses them if a suitable command of that name is not already available # in the command search PATH. : ${CP="cp -f"} : ${ECHO="printf %s\n"} : ${EGREP="$GREP -E"} : ${FGREP="$GREP -F"} : ${LN_S="ln -s"} : ${MAKE="make"} : ${MKDIR="mkdir"} : ${MV="mv -f"} : ${RM="rm -f"} : ${SHELL="${CONFIG_SHELL-/bin/sh}"} ## -------------------- ## ## Useful sed snippets. ## ## -------------------- ## sed_dirname='s|/[^/]*$||' sed_basename='s|^.*/||' # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='s|\([`"$\\]\)|\\\1|g' # Same as above, but do not quote variable references. sed_double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution that turns a string into a regex matching for the # string literally. sed_make_literal_regex='s|[].[^$\\*\/]|\\&|g' # Sed substitution that converts a w32 file name or path # that contains forward slashes, into one that contains # (escaped) backslashes. A very naive implementation. sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' # Re-'\' parameter expansions in output of sed_double_quote_subst that # were '\'-ed in input to the same. If an odd number of '\' preceded a # '$' in input to sed_double_quote_subst, that '$' was protected from # expansion. Since each input '\' is now two '\'s, look for any number # of runs of four '\'s followed by two '\'s and then a '$'. '\' that '$'. _G_bs='\\' _G_bs2='\\\\' _G_bs4='\\\\\\\\' _G_dollar='\$' sed_double_backslash="\ s/$_G_bs4/&\\ /g s/^$_G_bs2$_G_dollar/$_G_bs&/ s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g s/\n//g" ## ----------------- ## ## Global variables. ## ## ----------------- ## # Except for the global variables explicitly listed below, the following # functions in the '^func_' namespace, and the '^require_' namespace # variables initialised in the 'Resource management' section, sourcing # this file will not pollute your global namespace with anything # else. There's no portable way to scope variables in Bourne shell # though, so actually running these functions will sometimes place # results into a variable named after the function, and often use # temporary variables in the '^_G_' namespace. If you are careful to # avoid using those namespaces casually in your sourcing script, things # should continue to work as you expect. And, of course, you can freely # overwrite any of the functions or variables defined here before # calling anything to customize them. EXIT_SUCCESS=0 EXIT_FAILURE=1 EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. # Allow overriding, eg assuming that you follow the convention of # putting '$debug_cmd' at the start of all your functions, you can get # bash to show function call trace with: # # debug_cmd='eval echo "${FUNCNAME[0]} $*" >&2' bash your-script-name debug_cmd=${debug_cmd-":"} exit_cmd=: # By convention, finish your script with: # # exit $exit_status # # so that you can set exit_status to non-zero if you want to indicate # something went wrong during execution without actually bailing out at # the point of failure. exit_status=$EXIT_SUCCESS # Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh # is ksh but when the shell is invoked as "sh" and the current value of # the _XPG environment variable is not equal to 1 (one), the special # positional parameter $0, within a function call, is the name of the # function. progpath=$0 # The name of this program. progname=`$ECHO "$progpath" |$SED "$sed_basename"` # Make sure we have an absolute progpath for reexecution: case $progpath in [\\/]*|[A-Za-z]:\\*) ;; *[\\/]*) progdir=`$ECHO "$progpath" |$SED "$sed_dirname"` progdir=`cd "$progdir" && pwd` progpath=$progdir/$progname ;; *) _G_IFS=$IFS IFS=${PATH_SEPARATOR-:} for progdir in $PATH; do IFS=$_G_IFS test -x "$progdir/$progname" && break done IFS=$_G_IFS test -n "$progdir" || progdir=`pwd` progpath=$progdir/$progname ;; esac ## ----------------- ## ## Standard options. ## ## ----------------- ## # The following options affect the operation of the functions defined # below, and should be set appropriately depending on run-time para- # meters passed on the command line. opt_dry_run=false opt_quiet=false opt_verbose=false # Categories 'all' and 'none' are always available. Append any others # you will pass as the first argument to func_warning from your own # code. warning_categories= # By default, display warnings according to 'opt_warning_types'. Set # 'warning_func' to ':' to elide all warnings, or func_fatal_error to # treat the next displayed warning as a fatal error. warning_func=func_warn_and_continue # Set to 'all' to display all warnings, 'none' to suppress all # warnings, or a space delimited list of some subset of # 'warning_categories' to display only the listed warnings. opt_warning_types=all ## -------------------- ## ## Resource management. ## ## -------------------- ## # This section contains definitions for functions that each ensure a # particular resource (a file, or a non-empty configuration variable for # example) is available, and if appropriate to extract default values # from pertinent package files. Call them using their associated # 'require_*' variable to ensure that they are executed, at most, once. # # It's entirely deliberate that calling these functions can set # variables that don't obey the namespace limitations obeyed by the rest # of this file, in order that that they be as useful as possible to # callers. # require_term_colors # ------------------- # Allow display of bold text on terminals that support it. require_term_colors=func_require_term_colors func_require_term_colors () { $debug_cmd test -t 1 && { # COLORTERM and USE_ANSI_COLORS environment variables take # precedence, because most terminfo databases neglect to describe # whether color sequences are supported. test -n "${COLORTERM+set}" && : ${USE_ANSI_COLORS="1"} if test 1 = "$USE_ANSI_COLORS"; then # Standard ANSI escape sequences tc_reset='' tc_bold=''; tc_standout='' tc_red=''; tc_green='' tc_blue=''; tc_cyan='' else # Otherwise trust the terminfo database after all. test -n "`tput sgr0 2>/dev/null`" && { tc_reset=`tput sgr0` test -n "`tput bold 2>/dev/null`" && tc_bold=`tput bold` tc_standout=$tc_bold test -n "`tput smso 2>/dev/null`" && tc_standout=`tput smso` test -n "`tput setaf 1 2>/dev/null`" && tc_red=`tput setaf 1` test -n "`tput setaf 2 2>/dev/null`" && tc_green=`tput setaf 2` test -n "`tput setaf 4 2>/dev/null`" && tc_blue=`tput setaf 4` test -n "`tput setaf 5 2>/dev/null`" && tc_cyan=`tput setaf 5` } fi } require_term_colors=: } ## ----------------- ## ## Function library. ## ## ----------------- ## # This section contains a variety of useful functions to call in your # scripts. Take note of the portable wrappers for features provided by # some modern shells, which will fall back to slower equivalents on # less featureful shells. # func_append VAR VALUE # --------------------- # Append VALUE onto the existing contents of VAR. # We should try to minimise forks, especially on Windows where they are # unreasonably slow, so skip the feature probes when bash or zsh are # being used: if test set = "${BASH_VERSION+set}${ZSH_VERSION+set}"; then : ${_G_HAVE_ARITH_OP="yes"} : ${_G_HAVE_XSI_OPS="yes"} # The += operator was introduced in bash 3.1 case $BASH_VERSION in [12].* | 3.0 | 3.0*) ;; *) : ${_G_HAVE_PLUSEQ_OP="yes"} ;; esac fi # _G_HAVE_PLUSEQ_OP # Can be empty, in which case the shell is probed, "yes" if += is # useable or anything else if it does not work. test -z "$_G_HAVE_PLUSEQ_OP" \ && (eval 'x=a; x+=" b"; test "a b" = "$x"') 2>/dev/null \ && _G_HAVE_PLUSEQ_OP=yes if test yes = "$_G_HAVE_PLUSEQ_OP" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_append () { $debug_cmd eval "$1+=\$2" }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_append () { $debug_cmd eval "$1=\$$1\$2" } fi # func_append_quoted VAR VALUE # ---------------------------- # Quote VALUE and append to the end of shell variable VAR, separated # by a space. if test yes = "$_G_HAVE_PLUSEQ_OP"; then eval 'func_append_quoted () { $debug_cmd func_quote_arg pretty "$2" eval "$1+=\\ \$func_quote_arg_result" }' else func_append_quoted () { $debug_cmd func_quote_arg pretty "$2" eval "$1=\$$1\\ \$func_quote_arg_result" } fi # func_append_uniq VAR VALUE # -------------------------- # Append unique VALUE onto the existing contents of VAR, assuming # entries are delimited by the first character of VALUE. For example: # # func_append_uniq options " --another-option option-argument" # # will only append to $options if " --another-option option-argument " # is not already present somewhere in $options already (note spaces at # each end implied by leading space in second argument). func_append_uniq () { $debug_cmd eval _G_current_value='`$ECHO $'$1'`' _G_delim=`expr "$2" : '\(.\)'` case $_G_delim$_G_current_value$_G_delim in *"$2$_G_delim"*) ;; *) func_append "$@" ;; esac } # func_arith TERM... # ------------------ # Set func_arith_result to the result of evaluating TERMs. test -z "$_G_HAVE_ARITH_OP" \ && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \ && _G_HAVE_ARITH_OP=yes if test yes = "$_G_HAVE_ARITH_OP"; then eval 'func_arith () { $debug_cmd func_arith_result=$(( $* )) }' else func_arith () { $debug_cmd func_arith_result=`expr "$@"` } fi # func_basename FILE # ------------------ # Set func_basename_result to FILE with everything up to and including # the last / stripped. if test yes = "$_G_HAVE_XSI_OPS"; then # If this shell supports suffix pattern removal, then use it to avoid # forking. Hide the definitions single quotes in case the shell chokes # on unsupported syntax... _b='func_basename_result=${1##*/}' _d='case $1 in */*) func_dirname_result=${1%/*}$2 ;; * ) func_dirname_result=$3 ;; esac' else # ...otherwise fall back to using sed. _b='func_basename_result=`$ECHO "$1" |$SED "$sed_basename"`' _d='func_dirname_result=`$ECHO "$1" |$SED "$sed_dirname"` if test "X$func_dirname_result" = "X$1"; then func_dirname_result=$3 else func_append func_dirname_result "$2" fi' fi eval 'func_basename () { $debug_cmd '"$_b"' }' # func_dirname FILE APPEND NONDIR_REPLACEMENT # ------------------------------------------- # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. eval 'func_dirname () { $debug_cmd '"$_d"' }' # func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT # -------------------------------------------------------- # Perform func_basename and func_dirname in a single function # call: # dirname: Compute the dirname of FILE. If nonempty, # add APPEND to the result, otherwise set result # to NONDIR_REPLACEMENT. # value returned in "$func_dirname_result" # basename: Compute filename of FILE. # value retuned in "$func_basename_result" # For efficiency, we do not delegate to the functions above but instead # duplicate the functionality here. eval 'func_dirname_and_basename () { $debug_cmd '"$_b"' '"$_d"' }' # func_echo ARG... # ---------------- # Echo program name prefixed message. func_echo () { $debug_cmd _G_message=$* func_echo_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_IFS $ECHO "$progname: $_G_line" done IFS=$func_echo_IFS } # func_echo_all ARG... # -------------------- # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } # func_echo_infix_1 INFIX ARG... # ------------------------------ # Echo program name, followed by INFIX on the first line, with any # additional lines not showing INFIX. func_echo_infix_1 () { $debug_cmd $require_term_colors _G_infix=$1; shift _G_indent=$_G_infix _G_prefix="$progname: $_G_infix: " _G_message=$* # Strip color escape sequences before counting printable length for _G_tc in "$tc_reset" "$tc_bold" "$tc_standout" "$tc_red" "$tc_green" "$tc_blue" "$tc_cyan" do test -n "$_G_tc" && { _G_esc_tc=`$ECHO "$_G_tc" | $SED "$sed_make_literal_regex"` _G_indent=`$ECHO "$_G_indent" | $SED "s|$_G_esc_tc||g"` } done _G_indent="$progname: "`echo "$_G_indent" | $SED 's|.| |g'`" " ## exclude from sc_prohibit_nested_quotes func_echo_infix_1_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_infix_1_IFS $ECHO "$_G_prefix$tc_bold$_G_line$tc_reset" >&2 _G_prefix=$_G_indent done IFS=$func_echo_infix_1_IFS } # func_error ARG... # ----------------- # Echo program name prefixed message to standard error. func_error () { $debug_cmd $require_term_colors func_echo_infix_1 " $tc_standout${tc_red}error$tc_reset" "$*" >&2 } # func_fatal_error ARG... # ----------------------- # Echo program name prefixed message to standard error, and exit. func_fatal_error () { $debug_cmd func_error "$*" exit $EXIT_FAILURE } # func_grep EXPRESSION FILENAME # ----------------------------- # Check whether EXPRESSION matches any line of FILENAME, without output. func_grep () { $debug_cmd $GREP "$1" "$2" >/dev/null 2>&1 } # func_len STRING # --------------- # Set func_len_result to the length of STRING. STRING may not # start with a hyphen. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_len () { $debug_cmd func_len_result=${#1} }' else func_len () { $debug_cmd func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len` } fi # func_mkdir_p DIRECTORY-PATH # --------------------------- # Make sure the entire path to DIRECTORY-PATH is available. func_mkdir_p () { $debug_cmd _G_directory_path=$1 _G_dir_list= if test -n "$_G_directory_path" && test : != "$opt_dry_run"; then # Protect directory names starting with '-' case $_G_directory_path in -*) _G_directory_path=./$_G_directory_path ;; esac # While some portion of DIR does not yet exist... while test ! -d "$_G_directory_path"; do # ...make a list in topmost first order. Use a colon delimited # list incase some portion of path contains whitespace. _G_dir_list=$_G_directory_path:$_G_dir_list # If the last portion added has no slash in it, the list is done case $_G_directory_path in */*) ;; *) break ;; esac # ...otherwise throw away the child directory and loop _G_directory_path=`$ECHO "$_G_directory_path" | $SED -e "$sed_dirname"` done _G_dir_list=`$ECHO "$_G_dir_list" | $SED 's|:*$||'` func_mkdir_p_IFS=$IFS; IFS=: for _G_dir in $_G_dir_list; do IFS=$func_mkdir_p_IFS # mkdir can fail with a 'File exist' error if two processes # try to create one of the directories concurrently. Don't # stop in that case! $MKDIR "$_G_dir" 2>/dev/null || : done IFS=$func_mkdir_p_IFS # Bail out if we (or some other process) failed to create a directory. test -d "$_G_directory_path" || \ func_fatal_error "Failed to create '$1'" fi } # func_mktempdir [BASENAME] # ------------------------- # Make a temporary directory that won't clash with other running # libtool processes, and avoids race conditions if possible. If # given, BASENAME is the basename for that directory. func_mktempdir () { $debug_cmd _G_template=${TMPDIR-/tmp}/${1-$progname} if test : = "$opt_dry_run"; then # Return a directory name, but don't create it in dry-run mode _G_tmpdir=$_G_template-$$ else # If mktemp works, use that first and foremost _G_tmpdir=`mktemp -d "$_G_template-XXXXXXXX" 2>/dev/null` if test ! -d "$_G_tmpdir"; then # Failing that, at least try and use $RANDOM to avoid a race _G_tmpdir=$_G_template-${RANDOM-0}$$ func_mktempdir_umask=`umask` umask 0077 $MKDIR "$_G_tmpdir" umask $func_mktempdir_umask fi # If we're not in dry-run mode, bomb out on failure test -d "$_G_tmpdir" || \ func_fatal_error "cannot create temporary directory '$_G_tmpdir'" fi $ECHO "$_G_tmpdir" } # func_normal_abspath PATH # ------------------------ # Remove doubled-up and trailing slashes, "." path components, # and cancel out any ".." path components in PATH after making # it an absolute path. func_normal_abspath () { $debug_cmd # These SED scripts presuppose an absolute path with a trailing slash. _G_pathcar='s|^/\([^/]*\).*$|\1|' _G_pathcdr='s|^/[^/]*||' _G_removedotparts=':dotsl s|/\./|/|g t dotsl s|/\.$|/|' _G_collapseslashes='s|/\{1,\}|/|g' _G_finalslash='s|/*$|/|' # Start from root dir and reassemble the path. func_normal_abspath_result= func_normal_abspath_tpath=$1 func_normal_abspath_altnamespace= case $func_normal_abspath_tpath in "") # Empty path, that just means $cwd. func_stripname '' '/' "`pwd`" func_normal_abspath_result=$func_stripname_result return ;; # The next three entries are used to spot a run of precisely # two leading slashes without using negated character classes; # we take advantage of case's first-match behaviour. ///*) # Unusual form of absolute path, do nothing. ;; //*) # Not necessarily an ordinary path; POSIX reserves leading '//' # and for example Cygwin uses it to access remote file shares # over CIFS/SMB, so we conserve a leading double slash if found. func_normal_abspath_altnamespace=/ ;; /*) # Absolute path, do nothing. ;; *) # Relative path, prepend $cwd. func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath ;; esac # Cancel out all the simple stuff to save iterations. We also want # the path to end with a slash for ease of parsing, so make sure # there is one (and only one) here. func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_removedotparts" -e "$_G_collapseslashes" -e "$_G_finalslash"` while :; do # Processed it all yet? if test / = "$func_normal_abspath_tpath"; then # If we ascended to the root using ".." the result may be empty now. if test -z "$func_normal_abspath_result"; then func_normal_abspath_result=/ fi break fi func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcar"` func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcdr"` # Figure out what to do with it case $func_normal_abspath_tcomponent in "") # Trailing empty path component, ignore it. ;; ..) # Parent dir; strip last assembled component from result. func_dirname "$func_normal_abspath_result" func_normal_abspath_result=$func_dirname_result ;; *) # Actual path component, append it. func_append func_normal_abspath_result "/$func_normal_abspath_tcomponent" ;; esac done # Restore leading double-slash if one was found on entry. func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result } # func_notquiet ARG... # -------------------- # Echo program name prefixed message only when not in quiet mode. func_notquiet () { $debug_cmd $opt_quiet || func_echo ${1+"$@"} # A bug in bash halts the script if the last line of a function # fails when set -e is in force, so we need another command to # work around that: : } # func_relative_path SRCDIR DSTDIR # -------------------------------- # Set func_relative_path_result to the relative path from SRCDIR to DSTDIR. func_relative_path () { $debug_cmd func_relative_path_result= func_normal_abspath "$1" func_relative_path_tlibdir=$func_normal_abspath_result func_normal_abspath "$2" func_relative_path_tbindir=$func_normal_abspath_result # Ascend the tree starting from libdir while :; do # check if we have found a prefix of bindir case $func_relative_path_tbindir in $func_relative_path_tlibdir) # found an exact match func_relative_path_tcancelled= break ;; $func_relative_path_tlibdir*) # found a matching prefix func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" func_relative_path_tcancelled=$func_stripname_result if test -z "$func_relative_path_result"; then func_relative_path_result=. fi break ;; *) func_dirname $func_relative_path_tlibdir func_relative_path_tlibdir=$func_dirname_result if test -z "$func_relative_path_tlibdir"; then # Have to descend all the way to the root! func_relative_path_result=../$func_relative_path_result func_relative_path_tcancelled=$func_relative_path_tbindir break fi func_relative_path_result=../$func_relative_path_result ;; esac done # Now calculate path; take care to avoid doubling-up slashes. func_stripname '' '/' "$func_relative_path_result" func_relative_path_result=$func_stripname_result func_stripname '/' '/' "$func_relative_path_tcancelled" if test -n "$func_stripname_result"; then func_append func_relative_path_result "/$func_stripname_result" fi # Normalisation. If bindir is libdir, return '.' else relative path. if test -n "$func_relative_path_result"; then func_stripname './' '' "$func_relative_path_result" func_relative_path_result=$func_stripname_result fi test -n "$func_relative_path_result" || func_relative_path_result=. : } # func_quote_portable EVAL ARG # ---------------------------- # Internal function to portably implement func_quote_arg. Note that we still # keep attention to performance here so we as much as possible try to avoid # calling sed binary (so far O(N) complexity as long as func_append is O(1)). func_quote_portable () { $debug_cmd func_quote_portable_result=$2 # one-time-loop (easy break) while true do if $1; then func_quote_portable_result=`$ECHO "$2" | $SED \ -e "$sed_double_quote_subst" -e "$sed_double_backslash"` break fi # Quote for eval. case $func_quote_portable_result in *[\\\`\"\$]*) case $func_quote_portable_result in *[\[\*\?]*) func_quote_portable_result=`$ECHO "$func_quote_portable_result" \ | $SED "$sed_quote_subst"` break ;; esac func_quote_portable_old_IFS=$IFS for _G_char in '\' '`' '"' '$' do # STATE($1) PREV($2) SEPARATOR($3) set start "" "" func_quote_portable_result=dummy"$_G_char$func_quote_portable_result$_G_char"dummy IFS=$_G_char for _G_part in $func_quote_portable_result do case $1 in quote) func_append func_quote_portable_result "$3$2" set quote "$_G_part" "\\$_G_char" ;; start) set first "" "" func_quote_portable_result= ;; first) set quote "$_G_part" "" ;; esac done done IFS=$func_quote_portable_old_IFS ;; *) ;; esac break done func_quote_portable_unquoted_result=$func_quote_portable_result case $func_quote_portable_result in # double-quote args containing shell metacharacters to delay # word splitting, command substitution and variable expansion # for a subsequent eval. # many bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") func_quote_portable_result=\"$func_quote_portable_result\" ;; esac } # func_quotefast_eval ARG # ----------------------- # Quote one ARG (internal). This is equivalent to 'func_quote_arg eval ARG', # but optimized for speed. Result is stored in $func_quotefast_eval. if test xyes = `(x=; printf -v x %q yes; echo x"$x") 2>/dev/null`; then printf -v _GL_test_printf_tilde %q '~' if test '\~' = "$_GL_test_printf_tilde"; then func_quotefast_eval () { printf -v func_quotefast_eval_result %q "$1" } else # Broken older Bash implementations. Make those faster too if possible. func_quotefast_eval () { case $1 in '~'*) func_quote_portable false "$1" func_quotefast_eval_result=$func_quote_portable_result ;; *) printf -v func_quotefast_eval_result %q "$1" ;; esac } fi else func_quotefast_eval () { func_quote_portable false "$1" func_quotefast_eval_result=$func_quote_portable_result } fi # func_quote_arg MODEs ARG # ------------------------ # Quote one ARG to be evaled later. MODEs argument may contain zero or more # specifiers listed below separated by ',' character. This function returns two # values: # i) func_quote_arg_result # double-quoted (when needed), suitable for a subsequent eval # ii) func_quote_arg_unquoted_result # has all characters that are still active within double # quotes backslashified. Available only if 'unquoted' is specified. # # Available modes: # ---------------- # 'eval' (default) # - escape shell special characters # 'expand' # - the same as 'eval'; but do not quote variable references # 'pretty' # - request aesthetic output, i.e. '"a b"' instead of 'a\ b'. This might # be used later in func_quote to get output like: 'echo "a b"' instead # of 'echo a\ b'. This is slower than default on some shells. # 'unquoted' # - produce also $func_quote_arg_unquoted_result which does not contain # wrapping double-quotes. # # Examples for 'func_quote_arg pretty,unquoted string': # # string | *_result | *_unquoted_result # ------------+-----------------------+------------------- # " | \" | \" # a b | "a b" | a b # "a b" | "\"a b\"" | \"a b\" # * | "*" | * # z="${x-$y}" | "z=\"\${x-\$y}\"" | z=\"\${x-\$y}\" # # Examples for 'func_quote_arg pretty,unquoted,expand string': # # string | *_result | *_unquoted_result # --------------+---------------------+-------------------- # z="${x-$y}" | "z=\"${x-$y}\"" | z=\"${x-$y}\" func_quote_arg () { _G_quote_expand=false case ,$1, in *,expand,*) _G_quote_expand=: ;; esac case ,$1, in *,pretty,*|*,expand,*|*,unquoted,*) func_quote_portable $_G_quote_expand "$2" func_quote_arg_result=$func_quote_portable_result func_quote_arg_unquoted_result=$func_quote_portable_unquoted_result ;; *) # Faster quote-for-eval for some shells. func_quotefast_eval "$2" func_quote_arg_result=$func_quotefast_eval_result ;; esac } # func_quote MODEs ARGs... # ------------------------ # Quote all ARGs to be evaled later and join them into single command. See # func_quote_arg's description for more info. func_quote () { $debug_cmd _G_func_quote_mode=$1 ; shift func_quote_result= while test 0 -lt $#; do func_quote_arg "$_G_func_quote_mode" "$1" if test -n "$func_quote_result"; then func_append func_quote_result " $func_quote_arg_result" else func_append func_quote_result "$func_quote_arg_result" fi shift done } # func_stripname PREFIX SUFFIX NAME # --------------------------------- # strip PREFIX and SUFFIX from NAME, and store in func_stripname_result. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_stripname () { $debug_cmd # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are # positional parameters, so assign one to ordinary variable first. func_stripname_result=$3 func_stripname_result=${func_stripname_result#"$1"} func_stripname_result=${func_stripname_result%"$2"} }' else func_stripname () { $debug_cmd case $2 in .*) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%\\\\$2\$%%"`;; *) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%$2\$%%"`;; esac } fi # func_show_eval CMD [FAIL_EXP] # ----------------------------- # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. func_show_eval () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} func_quote_arg pretty,expand "$_G_cmd" eval "func_notquiet $func_quote_arg_result" $opt_dry_run || { eval "$_G_cmd" _G_status=$? if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_show_eval_locale CMD [FAIL_EXP] # ------------------------------------ # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. Use the saved locale for evaluation. func_show_eval_locale () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} $opt_quiet || { func_quote_arg expand,pretty "$_G_cmd" eval "func_echo $func_quote_arg_result" } $opt_dry_run || { eval "$_G_user_locale $_G_cmd" _G_status=$? eval "$_G_safe_locale" if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_tr_sh # ---------- # Turn $1 into a string suitable for a shell variable name. # Result is stored in $func_tr_sh_result. All characters # not in the set a-zA-Z0-9_ are replaced with '_'. Further, # if $1 begins with a digit, a '_' is prepended as well. func_tr_sh () { $debug_cmd case $1 in [0-9]* | *[!a-zA-Z0-9_]*) func_tr_sh_result=`$ECHO "$1" | $SED -e 's/^\([0-9]\)/_\1/' -e 's/[^a-zA-Z0-9_]/_/g'` ;; * ) func_tr_sh_result=$1 ;; esac } # func_verbose ARG... # ------------------- # Echo program name prefixed message in verbose mode only. func_verbose () { $debug_cmd $opt_verbose && func_echo "$*" : } # func_warn_and_continue ARG... # ----------------------------- # Echo program name prefixed warning message to standard error. func_warn_and_continue () { $debug_cmd $require_term_colors func_echo_infix_1 "${tc_red}warning$tc_reset" "$*" >&2 } # func_warning CATEGORY ARG... # ---------------------------- # Echo program name prefixed warning message to standard error. Warning # messages can be filtered according to CATEGORY, where this function # elides messages where CATEGORY is not listed in the global variable # 'opt_warning_types'. func_warning () { $debug_cmd # CATEGORY must be in the warning_categories list! case " $warning_categories " in *" $1 "*) ;; *) func_internal_error "invalid warning category '$1'" ;; esac _G_category=$1 shift case " $opt_warning_types " in *" $_G_category "*) $warning_func ${1+"$@"} ;; esac } # func_sort_ver VER1 VER2 # ----------------------- # 'sort -V' is not generally available. # Note this deviates from the version comparison in automake # in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a # but this should suffice as we won't be specifying old # version formats or redundant trailing .0 in bootstrap.conf. # If we did want full compatibility then we should probably # use m4_version_compare from autoconf. func_sort_ver () { $debug_cmd printf '%s\n%s\n' "$1" "$2" \ | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n } # func_lt_ver PREV CURR # --------------------- # Return true if PREV and CURR are in the correct order according to # func_sort_ver, otherwise false. Use it like this: # # func_lt_ver "$prev_ver" "$proposed_ver" || func_fatal_error "..." func_lt_ver () { $debug_cmd test "x$1" = x`func_sort_ver "$1" "$2" | $SED 1q` } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: #! /bin/sh # A portable, pluggable option parser for Bourne shell. # Written by Gary V. Vaughan, 2010 # This is free software. There is NO warranty; not even for # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Copyright (C) 2010-2018 Bootstrap Authors # # This file is dual licensed under the terms of the MIT license # , and GPL version 3 or later # . You must apply one of # these licenses when using or redistributing this software or any of # the files within it. See the URLs above, or the file `LICENSE` # included in the Bootstrap distribution for the full license texts. # Please report bugs or propose patches to: # # Set a version string for this script. scriptversion=2018-07-24.06; # UTC ## ------ ## ## Usage. ## ## ------ ## # This file is a library for parsing options in your shell scripts along # with assorted other useful supporting features that you can make use # of too. # # For the simplest scripts you might need only: # # #!/bin/sh # . relative/path/to/funclib.sh # . relative/path/to/options-parser # scriptversion=1.0 # func_options ${1+"$@"} # eval set dummy "$func_options_result"; shift # ...rest of your script... # # In order for the '--version' option to work, you will need to have a # suitably formatted comment like the one at the top of this file # starting with '# Written by ' and ending with '# Copyright'. # # For '-h' and '--help' to work, you will also need a one line # description of your script's purpose in a comment directly above the # '# Written by ' line, like the one at the top of this file. # # The default options also support '--debug', which will turn on shell # execution tracing (see the comment above debug_cmd below for another # use), and '--verbose' and the func_verbose function to allow your script # to display verbose messages only when your user has specified # '--verbose'. # # After sourcing this file, you can plug in processing for additional # options by amending the variables from the 'Configuration' section # below, and following the instructions in the 'Option parsing' # section further down. ## -------------- ## ## Configuration. ## ## -------------- ## # You should override these variables in your script after sourcing this # file so that they reflect the customisations you have added to the # option parser. # The usage line for option parsing errors and the start of '-h' and # '--help' output messages. You can embed shell variables for delayed # expansion at the time the message is displayed, but you will need to # quote other shell meta-characters carefully to prevent them being # expanded when the contents are evaled. usage='$progpath [OPTION]...' # Short help message in response to '-h' and '--help'. Add to this or # override it after sourcing this library to reflect the full set of # options your script accepts. usage_message="\ --debug enable verbose shell tracing -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -v, --verbose verbosely report processing --version print version information and exit -h, --help print short or long help message and exit " # Additional text appended to 'usage_message' in response to '--help'. long_help_message=" Warning categories include: 'all' show all warnings 'none' turn off all the warnings 'error' warnings are treated as fatal errors" # Help message printed before fatal option parsing errors. fatal_help="Try '\$progname --help' for more information." ## ------------------------- ## ## Hook function management. ## ## ------------------------- ## # This section contains functions for adding, removing, and running hooks # in the main code. A hook is just a list of function names that can be # run in order later on. # func_hookable FUNC_NAME # ----------------------- # Declare that FUNC_NAME will run hooks added with # 'func_add_hook FUNC_NAME ...'. func_hookable () { $debug_cmd func_append hookable_fns " $1" } # func_add_hook FUNC_NAME HOOK_FUNC # --------------------------------- # Request that FUNC_NAME call HOOK_FUNC before it returns. FUNC_NAME must # first have been declared "hookable" by a call to 'func_hookable'. func_add_hook () { $debug_cmd case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not accept hook functions." ;; esac eval func_append ${1}_hooks '" $2"' } # func_remove_hook FUNC_NAME HOOK_FUNC # ------------------------------------ # Remove HOOK_FUNC from the list of hook functions to be called by # FUNC_NAME. func_remove_hook () { $debug_cmd eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`' } # func_propagate_result FUNC_NAME_A FUNC_NAME_B # --------------------------------------------- # If the *_result variable of FUNC_NAME_A _is set_, assign its value to # *_result variable of FUNC_NAME_B. func_propagate_result () { $debug_cmd func_propagate_result_result=: if eval "test \"\${${1}_result+set}\" = set" then eval "${2}_result=\$${1}_result" else func_propagate_result_result=false fi } # func_run_hooks FUNC_NAME [ARG]... # --------------------------------- # Run all hook functions registered to FUNC_NAME. # It's assumed that the list of hook functions contains nothing more # than a whitespace-delimited list of legal shell function names, and # no effort is wasted trying to catch shell meta-characters or preserve # whitespace. func_run_hooks () { $debug_cmd case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not support hook functions." ;; esac eval _G_hook_fns=\$$1_hooks; shift for _G_hook in $_G_hook_fns; do func_unset "${_G_hook}_result" eval $_G_hook '${1+"$@"}' func_propagate_result $_G_hook func_run_hooks if $func_propagate_result_result; then eval set dummy "$func_run_hooks_result"; shift fi done } ## --------------- ## ## Option parsing. ## ## --------------- ## # In order to add your own option parsing hooks, you must accept the # full positional parameter list from your hook function. You may remove # or edit any options that you action, and then pass back the remaining # unprocessed options in '_result', escaped # suitably for 'eval'. # # The '_result' variable is automatically unset # before your hook gets called; for best performance, only set the # *_result variable when necessary (i.e. don't call the 'func_quote' # function unnecessarily because it can be an expensive operation on some # machines). # # Like this: # # my_options_prep () # { # $debug_cmd # # # Extend the existing usage message. # usage_message=$usage_message' # -s, --silent don'\''t print informational messages # ' # # No change in '$@' (ignored completely by this hook). Leave # # my_options_prep_result variable intact. # } # func_add_hook func_options_prep my_options_prep # # # my_silent_option () # { # $debug_cmd # # args_changed=false # # # Note that, for efficiency, we parse as many options as we can # # recognise in a loop before passing the remainder back to the # # caller on the first unrecognised argument we encounter. # while test $# -gt 0; do # opt=$1; shift # case $opt in # --silent|-s) opt_silent=: # args_changed=: # ;; # # Separate non-argument short options: # -s*) func_split_short_opt "$_G_opt" # set dummy "$func_split_short_opt_name" \ # "-$func_split_short_opt_arg" ${1+"$@"} # shift # args_changed=: # ;; # *) # Make sure the first unrecognised option "$_G_opt" # # is added back to "$@" in case we need it later, # # if $args_changed was set to 'true'. # set dummy "$_G_opt" ${1+"$@"}; shift; break ;; # esac # done # # # Only call 'func_quote' here if we processed at least one argument. # if $args_changed; then # func_quote eval ${1+"$@"} # my_silent_option_result=$func_quote_result # fi # } # func_add_hook func_parse_options my_silent_option # # # my_option_validation () # { # $debug_cmd # # $opt_silent && $opt_verbose && func_fatal_help "\ # '--silent' and '--verbose' options are mutually exclusive." # } # func_add_hook func_validate_options my_option_validation # # You'll also need to manually amend $usage_message to reflect the extra # options you parse. It's preferable to append if you can, so that # multiple option parsing hooks can be added safely. # func_options_finish [ARG]... # ---------------------------- # Finishing the option parse loop (call 'func_options' hooks ATM). func_options_finish () { $debug_cmd func_run_hooks func_options ${1+"$@"} func_propagate_result func_run_hooks func_options_finish } # func_options [ARG]... # --------------------- # All the functions called inside func_options are hookable. See the # individual implementations for details. func_hookable func_options func_options () { $debug_cmd _G_options_quoted=false for my_func in options_prep parse_options validate_options options_finish do func_unset func_${my_func}_result func_unset func_run_hooks_result eval func_$my_func '${1+"$@"}' func_propagate_result func_$my_func func_options if $func_propagate_result_result; then eval set dummy "$func_options_result"; shift _G_options_quoted=: fi done $_G_options_quoted || { # As we (func_options) are top-level options-parser function and # nobody quoted "$@" for us yet, we need to do it explicitly for # caller. func_quote eval ${1+"$@"} func_options_result=$func_quote_result } } # func_options_prep [ARG]... # -------------------------- # All initialisations required before starting the option parse loop. # Note that when calling hook functions, we pass through the list of # positional parameters. If a hook function modifies that list, and # needs to propagate that back to rest of this script, then the complete # modified list must be put in 'func_run_hooks_result' before returning. func_hookable func_options_prep func_options_prep () { $debug_cmd # Option defaults: opt_verbose=false opt_warning_types= func_run_hooks func_options_prep ${1+"$@"} func_propagate_result func_run_hooks func_options_prep } # func_parse_options [ARG]... # --------------------------- # The main option parsing loop. func_hookable func_parse_options func_parse_options () { $debug_cmd _G_parse_options_requote=false # this just eases exit handling while test $# -gt 0; do # Defer to hook functions for initial option parsing, so they # get priority in the event of reusing an option name. func_run_hooks func_parse_options ${1+"$@"} func_propagate_result func_run_hooks func_parse_options if $func_propagate_result_result; then eval set dummy "$func_parse_options_result"; shift # Even though we may have changed "$@", we passed the "$@" array # down into the hook and it quoted it for us (because we are in # this if-branch). No need to quote it again. _G_parse_options_requote=false fi # Break out of the loop if we already parsed every option. test $# -gt 0 || break # We expect that one of the options parsed in this function matches # and thus we remove _G_opt from "$@" and need to re-quote. _G_match_parse_options=: _G_opt=$1 shift case $_G_opt in --debug|-x) debug_cmd='set -x' func_echo "enabling shell trace mode" >&2 $debug_cmd ;; --no-warnings|--no-warning|--no-warn) set dummy --warnings none ${1+"$@"} shift ;; --warnings|--warning|-W) if test $# = 0 && func_missing_arg $_G_opt; then _G_parse_options_requote=: break fi case " $warning_categories $1" in *" $1 "*) # trailing space prevents matching last $1 above func_append_uniq opt_warning_types " $1" ;; *all) opt_warning_types=$warning_categories ;; *none) opt_warning_types=none warning_func=: ;; *error) opt_warning_types=$warning_categories warning_func=func_fatal_error ;; *) func_fatal_error \ "unsupported warning category: '$1'" ;; esac shift ;; --verbose|-v) opt_verbose=: ;; --version) func_version ;; -\?|-h) func_usage ;; --help) func_help ;; # Separate optargs to long options (plugins may need this): --*=*) func_split_equals "$_G_opt" set dummy "$func_split_equals_lhs" \ "$func_split_equals_rhs" ${1+"$@"} shift ;; # Separate optargs to short options: -W*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "$func_split_short_opt_arg" ${1+"$@"} shift ;; # Separate non-argument short options: -\?*|-h*|-v*|-x*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "-$func_split_short_opt_arg" ${1+"$@"} shift ;; --) _G_parse_options_requote=: ; break ;; -*) func_fatal_help "unrecognised option: '$_G_opt'" ;; *) set dummy "$_G_opt" ${1+"$@"}; shift _G_match_parse_options=false break ;; esac if $_G_match_parse_options; then _G_parse_options_requote=: fi done if $_G_parse_options_requote; then # save modified positional parameters for caller func_quote eval ${1+"$@"} func_parse_options_result=$func_quote_result fi } # func_validate_options [ARG]... # ------------------------------ # Perform any sanity checks on option settings and/or unconsumed # arguments. func_hookable func_validate_options func_validate_options () { $debug_cmd # Display all warnings if -W was not given. test -n "$opt_warning_types" || opt_warning_types=" $warning_categories" func_run_hooks func_validate_options ${1+"$@"} func_propagate_result func_run_hooks func_validate_options # Bail if the options were screwed! $exit_cmd $EXIT_FAILURE } ## ----------------- ## ## Helper functions. ## ## ----------------- ## # This section contains the helper functions used by the rest of the # hookable option parser framework in ascii-betical order. # func_fatal_help ARG... # ---------------------- # Echo program name prefixed message to standard error, followed by # a help hint, and exit. func_fatal_help () { $debug_cmd eval \$ECHO \""Usage: $usage"\" eval \$ECHO \""$fatal_help"\" func_error ${1+"$@"} exit $EXIT_FAILURE } # func_help # --------- # Echo long help message to standard output and exit. func_help () { $debug_cmd func_usage_message $ECHO "$long_help_message" exit 0 } # func_missing_arg ARGNAME # ------------------------ # Echo program name prefixed message to standard error and set global # exit_cmd. func_missing_arg () { $debug_cmd func_error "Missing argument for '$1'." exit_cmd=exit } # func_split_equals STRING # ------------------------ # Set func_split_equals_lhs and func_split_equals_rhs shell variables # after splitting STRING at the '=' sign. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_equals () { $debug_cmd func_split_equals_lhs=${1%%=*} func_split_equals_rhs=${1#*=} if test "x$func_split_equals_lhs" = "x$1"; then func_split_equals_rhs= fi }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_equals () { $debug_cmd func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'` func_split_equals_rhs= test "x$func_split_equals_lhs" = "x$1" \ || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'` } fi #func_split_equals # func_split_short_opt SHORTOPT # ----------------------------- # Set func_split_short_opt_name and func_split_short_opt_arg shell # variables after splitting SHORTOPT after the 2nd character. if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_short_opt () { $debug_cmd func_split_short_opt_arg=${1#??} func_split_short_opt_name=${1%"$func_split_short_opt_arg"} }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_short_opt () { $debug_cmd func_split_short_opt_name=`expr "x$1" : 'x-\(.\)'` func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'` } fi #func_split_short_opt # func_usage # ---------- # Echo short help message to standard output and exit. func_usage () { $debug_cmd func_usage_message $ECHO "Run '$progname --help |${PAGER-more}' for full usage" exit 0 } # func_usage_message # ------------------ # Echo short help message to standard output. func_usage_message () { $debug_cmd eval \$ECHO \""Usage: $usage"\" echo $SED -n 's|^# || /^Written by/{ x;p;x } h /^Written by/q' < "$progpath" echo eval \$ECHO \""$usage_message"\" } # func_version # ------------ # Echo version message to standard output and exit. # The version message is extracted from the calling file's header # comments, with leading '# ' stripped: # 1. First display the progname and version # 2. Followed by the header comment line matching /^# Written by / # 3. Then a blank line followed by the first following line matching # /^# Copyright / # 4. Immediately followed by any lines between the previous matches, # except lines preceding the intervening completely blank line. # For example, see the header comments of this file. func_version () { $debug_cmd printf '%s\n' "$progname $scriptversion" $SED -n ' /^# Written by /!b s|^# ||; p; n :fwd2blnk /./ { n b fwd2blnk } p; n :holdwrnt s|^# || s|^# *$|| /^Copyright /!{ /./H n b holdwrnt } s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2| G s|\(\n\)\n*|\1|g p; q' < "$progpath" exit $? } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "30/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: # Set a version string. scriptversion='(GNU libtool) 2.4.6.42-b88ce' # func_echo ARG... # ---------------- # Libtool also displays the current mode in messages, so override # funclib.sh func_echo with this custom definition. func_echo () { $debug_cmd _G_message=$* func_echo_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_IFS $ECHO "$progname${opt_mode+: $opt_mode}: $_G_line" done IFS=$func_echo_IFS } # func_warning ARG... # ------------------- # Libtool warnings are not categorized, so override funclib.sh # func_warning with this simpler definition. func_warning () { $debug_cmd $warning_func ${1+"$@"} } ## ---------------- ## ## Options parsing. ## ## ---------------- ## # Hook in the functions to make sure our own options are parsed during # the option parsing loop. usage='$progpath [OPTION]... [MODE-ARG]...' # Short help message in response to '-h'. usage_message="Options: --config show all configuration variables --debug enable verbose shell tracing -n, --dry-run display commands without modifying any files --features display basic configuration information and exit --mode=MODE use operation mode MODE --no-warnings equivalent to '-Wnone' --preserve-dup-deps don't remove duplicate dependency libraries --quiet, --silent don't print informational messages --tag=TAG use configuration variables from tag TAG -v, --verbose print more informational messages than default --version print version information -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -h, --help, --help-all print short, long, or detailed help message " # Additional text appended to 'usage_message' in response to '--help'. func_help () { $debug_cmd func_usage_message $ECHO "$long_help_message MODE must be one of the following: clean remove files from the build directory compile compile a source file into a libtool object execute automatically set library path, then run a program finish complete the installation of libtool libraries install install libraries or executables link create a library or an executable uninstall remove libraries from an installed directory MODE-ARGS vary depending on the MODE. When passed as first option, '--mode=MODE' may be abbreviated as 'MODE' or a unique abbreviation of that. Try '$progname --help --mode=MODE' for a more detailed description of MODE. When reporting a bug, please describe a test case to reproduce it and include the following information: host-triplet: $host shell: $SHELL compiler: $LTCC compiler flags: $LTCFLAGS linker: $LD (gnu? $with_gnu_ld) version: $progname (GNU libtool) 2.4.6.42-b88ce automake: `($AUTOMAKE --version) 2>/dev/null |$SED 1q` autoconf: `($AUTOCONF --version) 2>/dev/null |$SED 1q` Report bugs to . GNU libtool home page: . General help using GNU software: ." exit 0 } # func_lo2o OBJECT-NAME # --------------------- # Transform OBJECT-NAME from a '.lo' suffix to the platform specific # object suffix. lo2o=s/\\.lo\$/.$objext/ o2lo=s/\\.$objext\$/.lo/ if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_lo2o () { case $1 in *.lo) func_lo2o_result=${1%.lo}.$objext ;; * ) func_lo2o_result=$1 ;; esac }' # func_xform LIBOBJ-OR-SOURCE # --------------------------- # Transform LIBOBJ-OR-SOURCE from a '.o' or '.c' (or otherwise) # suffix to a '.lo' libtool-object suffix. eval 'func_xform () { func_xform_result=${1%.*}.lo }' else # ...otherwise fall back to using sed. func_lo2o () { func_lo2o_result=`$ECHO "$1" | $SED "$lo2o"` } func_xform () { func_xform_result=`$ECHO "$1" | $SED 's|\.[^.]*$|.lo|'` } fi # func_fatal_configuration ARG... # ------------------------------- # Echo program name prefixed message to standard error, followed by # a configuration failure hint, and exit. func_fatal_configuration () { func_fatal_error ${1+"$@"} \ "See the $PACKAGE documentation for more information." \ "Fatal configuration error." } # func_config # ----------- # Display the configuration for all the tags in this script. func_config () { re_begincf='^# ### BEGIN LIBTOOL' re_endcf='^# ### END LIBTOOL' # Default configuration. $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" # Now print the configurations for the tags. for tagname in $taglist; do $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" done exit $? } # func_features # ------------- # Display the features supported by this script. func_features () { echo "host: $host" if test yes = "$build_libtool_libs"; then echo "enable shared libraries" else echo "disable shared libraries" fi if test yes = "$build_old_libs"; then echo "enable static libraries" else echo "disable static libraries" fi exit $? } # func_enable_tag TAGNAME # ----------------------- # Verify that TAGNAME is valid, and either flag an error and exit, or # enable the TAGNAME tag. We also add TAGNAME to the global $taglist # variable here. func_enable_tag () { # Global variable: tagname=$1 re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" sed_extractcf=/$re_begincf/,/$re_endcf/p # Validate tagname. case $tagname in *[!-_A-Za-z0-9,/]*) func_fatal_error "invalid tag name: $tagname" ;; esac # Don't test for the "default" C tag, as we know it's # there but not specially marked. case $tagname in CC) ;; *) if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then taglist="$taglist $tagname" # Evaluate the configuration. Be careful to quote the path # and the sed script, to avoid splitting on whitespace, but # also don't use non-portable quotes within backquotes within # quotes we have to do it in 2 steps: extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` eval "$extractedcf" else func_error "ignoring unknown tag $tagname" fi ;; esac } # func_check_version_match # ------------------------ # Ensure that we are using m4 macros, and libtool script from the same # release of libtool. func_check_version_match () { if test "$package_revision" != "$macro_revision"; then if test "$VERSION" != "$macro_version"; then if test -z "$macro_version"; then cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from an older release. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from $PACKAGE $macro_version. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF fi else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, $progname: but the definition of this LT_INIT comes from revision $macro_revision. $progname: You should recreate aclocal.m4 with macros from revision $package_revision $progname: of $PACKAGE $VERSION and run autoconf again. _LT_EOF fi exit $EXIT_MISMATCH fi } # libtool_options_prep [ARG]... # ----------------------------- # Preparation for options parsed by libtool. libtool_options_prep () { $debug_mode # Option defaults: opt_config=false opt_dlopen= opt_dry_run=false opt_help=false opt_mode= opt_preserve_dup_deps=false opt_quiet=false nonopt= preserve_args= _G_rc_lt_options_prep=: # Shorthand for --mode=foo, only valid as the first argument case $1 in clean|clea|cle|cl) shift; set dummy --mode clean ${1+"$@"}; shift ;; compile|compil|compi|comp|com|co|c) shift; set dummy --mode compile ${1+"$@"}; shift ;; execute|execut|execu|exec|exe|ex|e) shift; set dummy --mode execute ${1+"$@"}; shift ;; finish|finis|fini|fin|fi|f) shift; set dummy --mode finish ${1+"$@"}; shift ;; install|instal|insta|inst|ins|in|i) shift; set dummy --mode install ${1+"$@"}; shift ;; link|lin|li|l) shift; set dummy --mode link ${1+"$@"}; shift ;; uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) shift; set dummy --mode uninstall ${1+"$@"}; shift ;; *) _G_rc_lt_options_prep=false ;; esac if $_G_rc_lt_options_prep; then # Pass back the list of options. func_quote eval ${1+"$@"} libtool_options_prep_result=$func_quote_result fi } func_add_hook func_options_prep libtool_options_prep # libtool_parse_options [ARG]... # --------------------------------- # Provide handling for libtool specific options. libtool_parse_options () { $debug_cmd _G_rc_lt_parse_options=false # Perform our own loop to consume as many options as possible in # each iteration. while test $# -gt 0; do _G_match_lt_parse_options=: _G_opt=$1 shift case $_G_opt in --dry-run|--dryrun|-n) opt_dry_run=: ;; --config) func_config ;; --dlopen|-dlopen) opt_dlopen="${opt_dlopen+$opt_dlopen }$1" shift ;; --preserve-dup-deps) opt_preserve_dup_deps=: ;; --features) func_features ;; --finish) set dummy --mode finish ${1+"$@"}; shift ;; --help) opt_help=: ;; --help-all) opt_help=': help-all' ;; --mode) test $# = 0 && func_missing_arg $_G_opt && break opt_mode=$1 case $1 in # Valid mode arguments: clean|compile|execute|finish|install|link|relink|uninstall) ;; # Catch anything else as an error *) func_error "invalid argument for $_G_opt" exit_cmd=exit break ;; esac shift ;; --no-silent|--no-quiet) opt_quiet=false func_append preserve_args " $_G_opt" ;; --no-warnings|--no-warning|--no-warn) opt_warning=false func_append preserve_args " $_G_opt" ;; --no-verbose) opt_verbose=false func_append preserve_args " $_G_opt" ;; --silent|--quiet) opt_quiet=: opt_verbose=false func_append preserve_args " $_G_opt" ;; --tag) test $# = 0 && func_missing_arg $_G_opt && break opt_tag=$1 func_append preserve_args " $_G_opt $1" func_enable_tag "$1" shift ;; --verbose|-v) opt_quiet=false opt_verbose=: func_append preserve_args " $_G_opt" ;; # An option not handled by this hook function: *) set dummy "$_G_opt" ${1+"$@"} ; shift _G_match_lt_parse_options=false break ;; esac $_G_match_lt_parse_options && _G_rc_lt_parse_options=: done if $_G_rc_lt_parse_options; then # save modified positional parameters for caller func_quote eval ${1+"$@"} libtool_parse_options_result=$func_quote_result fi } func_add_hook func_parse_options libtool_parse_options # libtool_validate_options [ARG]... # --------------------------------- # Perform any sanity checks on option settings and/or unconsumed # arguments. libtool_validate_options () { # save first non-option argument if test 0 -lt $#; then nonopt=$1 shift fi # preserve --debug test : = "$debug_cmd" || func_append preserve_args " --debug" case $host in # Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452 # see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788 *cygwin* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*) # don't eliminate duplications in $postdeps and $predeps opt_duplicate_compiler_generated_deps=: ;; *) opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps ;; esac $opt_help || { # Sanity checks first: func_check_version_match test yes != "$build_libtool_libs" \ && test yes != "$build_old_libs" \ && func_fatal_configuration "not configured to build any kind of library" # Darwin sucks eval std_shrext=\"$shrext_cmds\" # Only execute mode is allowed to have -dlopen flags. if test -n "$opt_dlopen" && test execute != "$opt_mode"; then func_error "unrecognized option '-dlopen'" $ECHO "$help" 1>&2 exit $EXIT_FAILURE fi # Change the help message to a mode-specific one. generic_help=$help help="Try '$progname --help --mode=$opt_mode' for more information." } # Pass back the unparsed argument list func_quote eval ${1+"$@"} libtool_validate_options_result=$func_quote_result } func_add_hook func_validate_options libtool_validate_options # Process options as early as possible so that --help and --version # can return quickly. func_options ${1+"$@"} eval set dummy "$func_options_result"; shift ## ----------- ## ## Main. ## ## ----------- ## magic='%%%MAGIC variable%%%' magic_exe='%%%MAGIC EXE variable%%%' # Global variables. extracted_archives= extracted_serial=0 # If this variable is set in any of the actions, the command in it # will be execed at the end. This prevents here-documents from being # left over by shells. exec_cmd= # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } # func_generated_by_libtool # True iff stdin has been generated by Libtool. This function is only # a basic sanity check; it will hardly flush out determined imposters. func_generated_by_libtool_p () { $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 } # func_lalib_p file # True iff FILE is a libtool '.la' library or '.lo' object file. # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_lalib_p () { test -f "$1" && $SED -e 4q "$1" 2>/dev/null | func_generated_by_libtool_p } # func_lalib_unsafe_p file # True iff FILE is a libtool '.la' library or '.lo' object file. # This function implements the same check as func_lalib_p without # resorting to external programs. To this end, it redirects stdin and # closes it afterwards, without saving the original file descriptor. # As a safety measure, use it only where a negative result would be # fatal anyway. Works if 'file' does not exist. func_lalib_unsafe_p () { lalib_p=no if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then for lalib_p_l in 1 2 3 4 do read lalib_p_line case $lalib_p_line in \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; esac done exec 0<&5 5<&- fi test yes = "$lalib_p" } # func_ltwrapper_script_p file # True iff FILE is a libtool wrapper script # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_script_p () { test -f "$1" && $lt_truncate_bin < "$1" 2>/dev/null | func_generated_by_libtool_p } # func_ltwrapper_executable_p file # True iff FILE is a libtool wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_executable_p () { func_ltwrapper_exec_suffix= case $1 in *.exe) ;; *) func_ltwrapper_exec_suffix=.exe ;; esac $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 } # func_ltwrapper_scriptname file # Assumes file is an ltwrapper_executable # uses $file to determine the appropriate filename for a # temporary ltwrapper_script. func_ltwrapper_scriptname () { func_dirname_and_basename "$1" "" "." func_stripname '' '.exe' "$func_basename_result" func_ltwrapper_scriptname_result=$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper } # func_ltwrapper_p file # True iff FILE is a libtool wrapper script or wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_p () { func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" } # func_execute_cmds commands fail_cmd # Execute tilde-delimited COMMANDS. # If FAIL_CMD is given, eval that upon failure. # FAIL_CMD may read-access the current command in variable CMD! func_execute_cmds () { $debug_cmd save_ifs=$IFS; IFS='~' for cmd in $1; do IFS=$sp$nl eval cmd=\"$cmd\" IFS=$save_ifs func_show_eval "$cmd" "${2-:}" done IFS=$save_ifs } # func_source file # Source FILE, adding directory component if necessary. # Note that it is not necessary on cygwin/mingw to append a dot to # FILE even if both FILE and FILE.exe exist: automatic-append-.exe # behavior happens only for exec(3), not for open(2)! Also, sourcing # 'FILE.' does not work on cygwin managed mounts. func_source () { $debug_cmd case $1 in */* | *\\*) . "$1" ;; *) . "./$1" ;; esac } # func_resolve_sysroot PATH # Replace a leading = in PATH with a sysroot. Store the result into # func_resolve_sysroot_result func_resolve_sysroot () { func_resolve_sysroot_result=$1 case $func_resolve_sysroot_result in =*) func_stripname '=' '' "$func_resolve_sysroot_result" func_resolve_sysroot_result=$lt_sysroot$func_stripname_result ;; esac } # func_replace_sysroot PATH # If PATH begins with the sysroot, replace it with = and # store the result into func_replace_sysroot_result. func_replace_sysroot () { case $lt_sysroot:$1 in ?*:"$lt_sysroot"*) func_stripname "$lt_sysroot" '' "$1" func_replace_sysroot_result='='$func_stripname_result ;; *) # Including no sysroot. func_replace_sysroot_result=$1 ;; esac } # func_infer_tag arg # Infer tagged configuration to use if any are available and # if one wasn't chosen via the "--tag" command line option. # Only attempt this if the compiler in the base compile # command doesn't match the default compiler. # arg is usually of the form 'gcc ...' func_infer_tag () { $debug_cmd if test -n "$available_tags" && test -z "$tagname"; then CC_quoted= for arg in $CC; do func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case $@ in # Blanks in the command may have been stripped by the calling shell, # but not from the CC environment variable when configure was run. " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; # Blanks at the start of $base_compile will cause this to fail # if we don't check for them as well. *) for z in $available_tags; do if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then # Evaluate the configuration. eval "`$SED -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" CC_quoted= for arg in $CC; do # Double-quote args containing other shell metacharacters. func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case "$@ " in " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) # The compiler in the base compile command matches # the one in the tagged configuration. # Assume this is the tagged configuration we want. tagname=$z break ;; esac fi done # If $tagname still isn't set, then no tagged configuration # was found and let the user know that the "--tag" command # line option must be used. if test -z "$tagname"; then func_echo "unable to infer tagged configuration" func_fatal_error "specify a tag with '--tag'" # else # func_verbose "using $tagname tagged configuration" fi ;; esac fi } # func_write_libtool_object output_name pic_name nonpic_name # Create a libtool object file (analogous to a ".la" file), # but don't create it if we're doing a dry run. func_write_libtool_object () { write_libobj=$1 if test yes = "$build_libtool_libs"; then write_lobj=\'$2\' else write_lobj=none fi if test yes = "$build_old_libs"; then write_oldobj=\'$3\' else write_oldobj=none fi $opt_dry_run || { cat >${write_libobj}T </dev/null` if test "$?" -eq 0 && test -n "$func_convert_core_file_wine_to_w32_tmp"; then func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | $SED -e "$sed_naive_backslashify"` else func_convert_core_file_wine_to_w32_result= fi fi } # end: func_convert_core_file_wine_to_w32 # func_convert_core_path_wine_to_w32 ARG # Helper function used by path conversion functions when $build is *nix, and # $host is mingw, cygwin, or some other w32 environment. Relies on a correctly # configured wine environment available, with the winepath program in $build's # $PATH. Assumes ARG has no leading or trailing path separator characters. # # ARG is path to be converted from $build format to win32. # Result is available in $func_convert_core_path_wine_to_w32_result. # Unconvertible file (directory) names in ARG are skipped; if no directory names # are convertible, then the result may be empty. func_convert_core_path_wine_to_w32 () { $debug_cmd # unfortunately, winepath doesn't convert paths, only file names func_convert_core_path_wine_to_w32_result= if test -n "$1"; then oldIFS=$IFS IFS=: for func_convert_core_path_wine_to_w32_f in $1; do IFS=$oldIFS func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" if test -n "$func_convert_core_file_wine_to_w32_result"; then if test -z "$func_convert_core_path_wine_to_w32_result"; then func_convert_core_path_wine_to_w32_result=$func_convert_core_file_wine_to_w32_result else func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" fi fi done IFS=$oldIFS fi } # end: func_convert_core_path_wine_to_w32 # func_cygpath ARGS... # Wrapper around calling the cygpath program via LT_CYGPATH. This is used when # when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) # $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or # (2), returns the Cygwin file name or path in func_cygpath_result (input # file name or path is assumed to be in w32 format, as previously converted # from $build's *nix or MSYS format). In case (3), returns the w32 file name # or path in func_cygpath_result (input file name or path is assumed to be in # Cygwin format). Returns an empty string on error. # # ARGS are passed to cygpath, with the last one being the file name or path to # be converted. # # Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH # environment variable; do not put it in $PATH. func_cygpath () { $debug_cmd if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` if test "$?" -ne 0; then # on failure, ensure result is empty func_cygpath_result= fi else func_cygpath_result= func_error "LT_CYGPATH is empty or specifies non-existent file: '$LT_CYGPATH'" fi } #end: func_cygpath # func_convert_core_msys_to_w32 ARG # Convert file name or path ARG from MSYS format to w32 format. Return # result in func_convert_core_msys_to_w32_result. func_convert_core_msys_to_w32 () { $debug_cmd # awkward: cmd appends spaces to result func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | $SED -e 's/[ ]*$//' -e "$sed_naive_backslashify"` } #end: func_convert_core_msys_to_w32 # func_convert_file_check ARG1 ARG2 # Verify that ARG1 (a file name in $build format) was converted to $host # format in ARG2. Otherwise, emit an error message, but continue (resetting # func_to_host_file_result to ARG1). func_convert_file_check () { $debug_cmd if test -z "$2" && test -n "$1"; then func_error "Could not determine host file name corresponding to" func_error " '$1'" func_error "Continuing, but uninstalled executables may not work." # Fallback: func_to_host_file_result=$1 fi } # end func_convert_file_check # func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH # Verify that FROM_PATH (a path in $build format) was converted to $host # format in TO_PATH. Otherwise, emit an error message, but continue, resetting # func_to_host_file_result to a simplistic fallback value (see below). func_convert_path_check () { $debug_cmd if test -z "$4" && test -n "$3"; then func_error "Could not determine the host path corresponding to" func_error " '$3'" func_error "Continuing, but uninstalled executables may not work." # Fallback. This is a deliberately simplistic "conversion" and # should not be "improved". See libtool.info. if test "x$1" != "x$2"; then lt_replace_pathsep_chars="s|$1|$2|g" func_to_host_path_result=`echo "$3" | $SED -e "$lt_replace_pathsep_chars"` else func_to_host_path_result=$3 fi fi } # end func_convert_path_check # func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG # Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT # and appending REPL if ORIG matches BACKPAT. func_convert_path_front_back_pathsep () { $debug_cmd case $4 in $1 ) func_to_host_path_result=$3$func_to_host_path_result ;; esac case $4 in $2 ) func_append func_to_host_path_result "$3" ;; esac } # end func_convert_path_front_back_pathsep ################################################## # $build to $host FILE NAME CONVERSION FUNCTIONS # ################################################## # invoked via '$to_host_file_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # Result will be available in $func_to_host_file_result. # func_to_host_file ARG # Converts the file name ARG from $build format to $host format. Return result # in func_to_host_file_result. func_to_host_file () { $debug_cmd $to_host_file_cmd "$1" } # end func_to_host_file # func_to_tool_file ARG LAZY # converts the file name ARG from $build format to toolchain format. Return # result in func_to_tool_file_result. If the conversion in use is listed # in (the comma separated) LAZY, no conversion takes place. func_to_tool_file () { $debug_cmd case ,$2, in *,"$to_tool_file_cmd",*) func_to_tool_file_result=$1 ;; *) $to_tool_file_cmd "$1" func_to_tool_file_result=$func_to_host_file_result ;; esac } # end func_to_tool_file # func_convert_file_noop ARG # Copy ARG to func_to_host_file_result. func_convert_file_noop () { func_to_host_file_result=$1 } # end func_convert_file_noop # func_convert_file_msys_to_w32 ARG # Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_file_result. func_convert_file_msys_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_to_host_file_result=$func_convert_core_msys_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_w32 # func_convert_file_cygwin_to_w32 ARG # Convert file name ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_file_cygwin_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then # because $build is cygwin, we call "the" cygpath in $PATH; no need to use # LT_CYGPATH in this case. func_to_host_file_result=`cygpath -m "$1"` fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_cygwin_to_w32 # func_convert_file_nix_to_w32 ARG # Convert file name ARG from *nix to w32 format. Requires a wine environment # and a working winepath. Returns result in func_to_host_file_result. func_convert_file_nix_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_file_wine_to_w32 "$1" func_to_host_file_result=$func_convert_core_file_wine_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_w32 # func_convert_file_msys_to_cygwin ARG # Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_file_msys_to_cygwin () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_cygpath -u "$func_convert_core_msys_to_w32_result" func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_cygwin # func_convert_file_nix_to_cygwin ARG # Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed # in a wine environment, working winepath, and LT_CYGPATH set. Returns result # in func_to_host_file_result. func_convert_file_nix_to_cygwin () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. func_convert_core_file_wine_to_w32 "$1" func_cygpath -u "$func_convert_core_file_wine_to_w32_result" func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_cygwin ############################################# # $build to $host PATH CONVERSION FUNCTIONS # ############################################# # invoked via '$to_host_path_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # The result will be available in $func_to_host_path_result. # # Path separators are also converted from $build format to $host format. If # ARG begins or ends with a path separator character, it is preserved (but # converted to $host format) on output. # # All path conversion functions are named using the following convention: # file name conversion function : func_convert_file_X_to_Y () # path conversion function : func_convert_path_X_to_Y () # where, for any given $build/$host combination the 'X_to_Y' value is the # same. If conversion functions are added for new $build/$host combinations, # the two new functions must follow this pattern, or func_init_to_host_path_cmd # will break. # func_init_to_host_path_cmd # Ensures that function "pointer" variable $to_host_path_cmd is set to the # appropriate value, based on the value of $to_host_file_cmd. to_host_path_cmd= func_init_to_host_path_cmd () { $debug_cmd if test -z "$to_host_path_cmd"; then func_stripname 'func_convert_file_' '' "$to_host_file_cmd" to_host_path_cmd=func_convert_path_$func_stripname_result fi } # func_to_host_path ARG # Converts the path ARG from $build format to $host format. Return result # in func_to_host_path_result. func_to_host_path () { $debug_cmd func_init_to_host_path_cmd $to_host_path_cmd "$1" } # end func_to_host_path # func_convert_path_noop ARG # Copy ARG to func_to_host_path_result. func_convert_path_noop () { func_to_host_path_result=$1 } # end func_convert_path_noop # func_convert_path_msys_to_w32 ARG # Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_path_result. func_convert_path_msys_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from ARG. MSYS # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; # and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result=$func_convert_core_msys_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_msys_to_w32 # func_convert_path_cygwin_to_w32 ARG # Convert path ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_path_cygwin_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_cygwin_to_w32 # func_convert_path_nix_to_w32 ARG # Convert path ARG from *nix to w32 format. Requires a wine environment and # a working winepath. Returns result in func_to_host_file_result. func_convert_path_nix_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result=$func_convert_core_path_wine_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_nix_to_w32 # func_convert_path_msys_to_cygwin ARG # Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_path_msys_to_cygwin () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_msys_to_w32_result" func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_msys_to_cygwin # func_convert_path_nix_to_cygwin ARG # Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a # a wine environment, working winepath, and LT_CYGPATH set. Returns result in # func_to_host_file_result. func_convert_path_nix_to_cygwin () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from # ARG. msys behavior is inconsistent here, cygpath turns them # into '.;' and ';.', and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_nix_to_cygwin # func_dll_def_p FILE # True iff FILE is a Windows DLL '.def' file. # Keep in sync with _LT_DLL_DEF_P in libtool.m4 func_dll_def_p () { $debug_cmd func_dll_def_p_tmp=`$SED -n \ -e 's/^[ ]*//' \ -e '/^\(;.*\)*$/d' \ -e 's/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p' \ -e q \ "$1"` test DEF = "$func_dll_def_p_tmp" } # func_mode_compile arg... func_mode_compile () { $debug_cmd # Get the compilation command and the source file. base_compile= srcfile=$nonopt # always keep a non-empty value in "srcfile" suppress_opt=yes suppress_output= arg_mode=normal libobj= later= pie_flag= for arg do case $arg_mode in arg ) # do not "continue". Instead, add this to base_compile lastarg=$arg arg_mode=normal ;; target ) libobj=$arg arg_mode=normal continue ;; normal ) # Accept any command-line options. case $arg in -o) test -n "$libobj" && \ func_fatal_error "you cannot specify '-o' more than once" arg_mode=target continue ;; -pie | -fpie | -fPIE) func_append pie_flag " $arg" continue ;; -shared | -static | -prefer-pic | -prefer-non-pic) func_append later " $arg" continue ;; -no-suppress) suppress_opt=no continue ;; -Xcompiler) arg_mode=arg # the next one goes into the "base_compile" arg list continue # The current "srcfile" will either be retained or ;; # replaced later. I would guess that would be a bug. -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result lastarg= save_ifs=$IFS; IFS=, for arg in $args; do IFS=$save_ifs func_append_quoted lastarg "$arg" done IFS=$save_ifs func_stripname ' ' '' "$lastarg" lastarg=$func_stripname_result # Add the arguments to base_compile. func_append base_compile " $lastarg" continue ;; *) # Accept the current argument as the source file. # The previous "srcfile" becomes the current argument. # lastarg=$srcfile srcfile=$arg ;; esac # case $arg ;; esac # case $arg_mode # Aesthetically quote the previous argument. func_append_quoted base_compile "$lastarg" done # for arg case $arg_mode in arg) func_fatal_error "you must specify an argument for -Xcompile" ;; target) func_fatal_error "you must specify a target with '-o'" ;; *) # Get the name of the library object. test -z "$libobj" && { func_basename "$srcfile" libobj=$func_basename_result } ;; esac # Recognize several different file suffixes. # If the user specifies -o file.o, it is replaced with file.lo case $libobj in *.[cCFSifmso] | \ *.ada | *.adb | *.ads | *.asm | \ *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) func_xform "$libobj" libobj=$func_xform_result ;; esac case $libobj in *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; *) func_fatal_error "cannot determine name of library object from '$libobj'" ;; esac func_infer_tag $base_compile for arg in $later; do case $arg in -shared) test yes = "$build_libtool_libs" \ || func_fatal_configuration "cannot build a shared library" build_old_libs=no continue ;; -static) build_libtool_libs=no build_old_libs=yes continue ;; -prefer-pic) pic_mode=yes continue ;; -prefer-non-pic) pic_mode=no continue ;; esac done func_quote_arg pretty "$libobj" test "X$libobj" != "X$func_quote_arg_result" \ && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ && func_warning "libobj name '$libobj' may not contain shell special characters." func_dirname_and_basename "$obj" "/" "" objname=$func_basename_result xdir=$func_dirname_result lobj=$xdir$objdir/$objname test -z "$base_compile" && \ func_fatal_help "you must specify a compilation command" # Delete any leftover library objects. if test yes = "$build_old_libs"; then removelist="$obj $lobj $libobj ${libobj}T" else removelist="$lobj $libobj ${libobj}T" fi # On Cygwin there's no "real" PIC flag so we must build both object types case $host_os in cygwin* | mingw* | pw32* | os2* | cegcc*) pic_mode=default ;; esac if test no = "$pic_mode" && test pass_all != "$deplibs_check_method"; then # non-PIC code in shared libraries is not supported pic_mode=default fi # Calculate the filename of the output object if compiler does # not support -o with -c if test no = "$compiler_c_o"; then output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.$objext lockfile=$output_obj.lock else output_obj= need_locks=no lockfile= fi # Lock this critical section if it is needed # We use this script file to make the link, it avoids creating a new file if test yes = "$need_locks"; then until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done elif test warn = "$need_locks"; then if test -f "$lockfile"; then $ECHO "\ *** ERROR, $lockfile exists and contains: `cat $lockfile 2>/dev/null` This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi func_append removelist " $output_obj" $ECHO "$srcfile" > "$lockfile" fi $opt_dry_run || $RM $removelist func_append removelist " $lockfile" trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 srcfile=$func_to_tool_file_result func_quote_arg pretty "$srcfile" qsrcfile=$func_quote_arg_result # Only build a PIC object if we are building libtool libraries. if test yes = "$build_libtool_libs"; then # Without this assignment, base_compile gets emptied. fbsd_hideous_sh_bug=$base_compile if test no != "$pic_mode"; then command="$base_compile $qsrcfile $pic_flag" else # Don't build PIC code command="$base_compile $qsrcfile" fi func_mkdir_p "$xdir$objdir" if test -z "$output_obj"; then # Place PIC objects in $objdir func_append command " -o $lobj" fi func_show_eval_locale "$command" \ 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed, then go on to compile the next one if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then func_show_eval '$MV "$output_obj" "$lobj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi # Allow error messages only from the first compilation. if test yes = "$suppress_opt"; then suppress_output=' >/dev/null 2>&1' fi fi # Only build a position-dependent object if we build old libraries. if test yes = "$build_old_libs"; then if test yes != "$pic_mode"; then # Don't build PIC code command="$base_compile $qsrcfile$pie_flag" else command="$base_compile $qsrcfile $pic_flag" fi if test yes = "$compiler_c_o"; then func_append command " -o $obj" fi # Suppress compiler output if we already did a PIC compilation. func_append command "$suppress_output" func_show_eval_locale "$command" \ '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then func_show_eval '$MV "$output_obj" "$obj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi fi $opt_dry_run || { func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" # Unlock the critical section if it was locked if test no != "$need_locks"; then removelist=$lockfile $RM "$lockfile" fi } exit $EXIT_SUCCESS } $opt_help || { test compile = "$opt_mode" && func_mode_compile ${1+"$@"} } func_mode_help () { # We need to display help for each of the modes. case $opt_mode in "") # Generic help is extracted from the usage comments # at the start of this file. func_help ;; clean) $ECHO \ "Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... Remove files from the build directory. RM is the name of the program to use to delete files associated with each FILE (typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, object or program, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; compile) $ECHO \ "Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE Compile a source file into a libtool library object. This mode accepts the following additional options: -o OUTPUT-FILE set the output file name to OUTPUT-FILE -no-suppress do not suppress compiler output for multiple passes -prefer-pic try to build PIC objects only -prefer-non-pic try to build non-PIC objects only -shared do not build a '.o' file suitable for static linking -static only build a '.o' file suitable for static linking -Wc,FLAG pass FLAG directly to the compiler COMPILE-COMMAND is a command to be used in creating a 'standard' object file from the given SOURCEFILE. The output file name is determined by removing the directory component from SOURCEFILE, then substituting the C source code suffix '.c' with the library object suffix, '.lo'." ;; execute) $ECHO \ "Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... Automatically set library path, then run a program. This mode accepts the following additional options: -dlopen FILE add the directory containing FILE to the library path This mode sets the library path environment variable according to '-dlopen' flags. If any of the ARGS are libtool executable wrappers, then they are translated into their corresponding uninstalled binary, and any of their required library directories are added to the library path. Then, COMMAND is executed, with ARGS as arguments." ;; finish) $ECHO \ "Usage: $progname [OPTION]... --mode=finish [LIBDIR]... Complete the installation of libtool libraries. Each LIBDIR is a directory that contains libtool libraries. The commands that this mode executes may require superuser privileges. Use the '--dry-run' option if you just want to see what would be executed." ;; install) $ECHO \ "Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... Install executables or libraries. INSTALL-COMMAND is the installation command. The first component should be either the 'install' or 'cp' program. The following components of INSTALL-COMMAND are treated specially: -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation The rest of the components are interpreted as arguments to that command (only BSD-compatible install options are recognized)." ;; link) $ECHO \ "Usage: $progname [OPTION]... --mode=link LINK-COMMAND... Link object files or libraries together to form another library, or to create an executable program. LINK-COMMAND is a command using the C compiler that you would use to create a program from several object files. The following components of LINK-COMMAND are treated specially: -all-static do not do any dynamic linking at all -avoid-version do not add a version suffix if possible -bindir BINDIR specify path to binaries directory (for systems where libraries must be found in the PATH setting at runtime) -dlopen FILE '-dlpreopen' FILE if it cannot be dlopened at runtime -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) -export-symbols SYMFILE try to export only the symbols listed in SYMFILE -export-symbols-regex REGEX try to export only the symbols matching REGEX -LLIBDIR search LIBDIR for required installed libraries -lNAME OUTPUT-FILE requires the installed library libNAME -module build a library that can dlopened -no-fast-install disable the fast-install mode -no-install link a not-installable executable -no-undefined declare that a library does not refer to external symbols -o OUTPUT-FILE create OUTPUT-FILE from the specified objects -objectlist FILE use a list of object files found in FILE to specify objects -os2dllname NAME force a short DLL name on OS/2 (no effect on other OSes) -precious-files-regex REGEX don't remove output files matching REGEX -release RELEASE specify package release information -rpath LIBDIR the created library will eventually be installed in LIBDIR -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries -shared only do dynamic linking of libtool libraries -shrext SUFFIX override the standard shared library file extension -static do not do any dynamic linking of uninstalled libtool libraries -static-libtool-libs do not do any dynamic linking of libtool libraries -version-info CURRENT[:REVISION[:AGE]] specify library version info [each variable defaults to 0] -weak LIBNAME declare that the target provides the LIBNAME interface -Wc,FLAG -Xcompiler FLAG pass linker-specific FLAG directly to the compiler -Wl,FLAG -Xlinker FLAG pass linker-specific FLAG directly to the linker -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) All other options (arguments beginning with '-') are ignored. Every other argument is treated as a filename. Files ending in '.la' are treated as uninstalled libtool libraries, other files are standard or library object files. If the OUTPUT-FILE ends in '.la', then a libtool library is created, only library objects ('.lo' files) may be specified, and '-rpath' is required, except when creating a convenience library. If OUTPUT-FILE ends in '.a' or '.lib', then a standard library is created using 'ar' and 'ranlib', or on Windows using 'lib'. If OUTPUT-FILE ends in '.lo' or '.$objext', then a reloadable object file is created, otherwise an executable program is created." ;; uninstall) $ECHO \ "Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... Remove libraries from an installation directory. RM is the name of the program to use to delete files associated with each FILE (typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; *) func_fatal_help "invalid operation mode '$opt_mode'" ;; esac echo $ECHO "Try '$progname --help' for more information about other modes." } # Now that we've collected a possible --mode arg, show help if necessary if $opt_help; then if test : = "$opt_help"; then func_mode_help else { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do func_mode_help done } | $SED -n '1p; 2,$s/^Usage:/ or: /p' { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do echo func_mode_help done } | $SED '1d /^When reporting/,/^Report/{ H d } $x /information about other modes/d /more detailed .*MODE/d s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' fi exit $? fi # func_mode_execute arg... func_mode_execute () { $debug_cmd # The first argument is the command name. cmd=$nonopt test -z "$cmd" && \ func_fatal_help "you must specify a COMMAND" # Handle -dlopen flags immediately. for file in $opt_dlopen; do test -f "$file" \ || func_fatal_help "'$file' is not a file" dir= case $file in *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "'$lib' is not a valid libtool archive" # Read the libtool library. dlname= library_names= func_source "$file" # Skip this library if it cannot be dlopened. if test -z "$dlname"; then # Warn if it was a shared library. test -n "$library_names" && \ func_warning "'$file' was not linked with '-export-dynamic'" continue fi func_dirname "$file" "" "." dir=$func_dirname_result if test -f "$dir/$objdir/$dlname"; then func_append dir "/$objdir" else if test ! -f "$dir/$dlname"; then func_fatal_error "cannot find '$dlname' in '$dir' or '$dir/$objdir'" fi fi ;; *.lo) # Just add the directory containing the .lo file. func_dirname "$file" "" "." dir=$func_dirname_result ;; *) func_warning "'-dlopen' is ignored for non-libtool libraries and objects" continue ;; esac # Get the absolute pathname. absdir=`cd "$dir" && pwd` test -n "$absdir" && dir=$absdir # Now add the directory to shlibpath_var. if eval "test -z \"\$$shlibpath_var\""; then eval "$shlibpath_var=\"\$dir\"" else eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" fi done # This variable tells wrapper scripts just to set shlibpath_var # rather than running their programs. libtool_execute_magic=$magic # Check if any of the arguments is a wrapper script. args= for file do case $file in -* | *.la | *.lo ) ;; *) # Do a test to see if this is really a libtool program. if func_ltwrapper_script_p "$file"; then func_source "$file" # Transform arg to wrapped name. file=$progdir/$program elif func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" func_source "$func_ltwrapper_scriptname_result" # Transform arg to wrapped name. file=$progdir/$program fi ;; esac # Quote arguments (to preserve shell metacharacters). func_append_quoted args "$file" done if $opt_dry_run; then # Display what would be done. if test -n "$shlibpath_var"; then eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" echo "export $shlibpath_var" fi $ECHO "$cmd$args" exit $EXIT_SUCCESS else if test -n "$shlibpath_var"; then # Export the shlibpath_var. eval "export $shlibpath_var" fi # Restore saved environment variables for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test \"\${save_$lt_var+set}\" = set; then $lt_var=\$save_$lt_var; export $lt_var else $lt_unset $lt_var fi" done # Now prepare to actually exec the command. exec_cmd=\$cmd$args fi } test execute = "$opt_mode" && func_mode_execute ${1+"$@"} # func_mode_finish arg... func_mode_finish () { $debug_cmd libs= libdirs= admincmds= for opt in "$nonopt" ${1+"$@"} do if test -d "$opt"; then func_append libdirs " $opt" elif test -f "$opt"; then if func_lalib_unsafe_p "$opt"; then func_append libs " $opt" else func_warning "'$opt' is not a valid libtool archive" fi else func_fatal_error "invalid argument '$opt'" fi done if test -n "$libs"; then if test -n "$lt_sysroot"; then sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" else sysroot_cmd= fi # Remove sysroot references if $opt_dry_run; then for lib in $libs; do echo "removing references to $lt_sysroot and '=' prefixes from $lib" done else tmpdir=`func_mktempdir` for lib in $libs; do $SED -e "$sysroot_cmd s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ > $tmpdir/tmp-la mv -f $tmpdir/tmp-la $lib done ${RM}r "$tmpdir" fi fi if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then for libdir in $libdirs; do if test -n "$finish_cmds"; then # Do each command in the finish commands. func_execute_cmds "$finish_cmds" 'admincmds="$admincmds '"$cmd"'"' fi if test -n "$finish_eval"; then # Do the single finish_eval. eval cmds=\"$finish_eval\" $opt_dry_run || eval "$cmds" || func_append admincmds " $cmds" fi done fi # Exit here if they wanted silent mode. $opt_quiet && exit $EXIT_SUCCESS if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then echo "----------------------------------------------------------------------" echo "Libraries have been installed in:" for libdir in $libdirs; do $ECHO " $libdir" done echo echo "If you ever happen to want to link against installed libraries" echo "in a given directory, LIBDIR, you must either use libtool, and" echo "specify the full pathname of the library, or use the '-LLIBDIR'" echo "flag during linking and do at least one of the following:" if test -n "$shlibpath_var"; then echo " - add LIBDIR to the '$shlibpath_var' environment variable" echo " during execution" fi if test -n "$runpath_var"; then echo " - add LIBDIR to the '$runpath_var' environment variable" echo " during linking" fi if test -n "$hardcode_libdir_flag_spec"; then libdir=LIBDIR eval flag=\"$hardcode_libdir_flag_spec\" $ECHO " - use the '$flag' linker flag" fi if test -n "$admincmds"; then $ECHO " - have your system administrator run these commands:$admincmds" fi if test -f /etc/ld.so.conf; then echo " - have your system administrator add LIBDIR to '/etc/ld.so.conf'" fi echo echo "See any operating system documentation about shared libraries for" case $host in solaris2.[6789]|solaris2.1[0-9]) echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" echo "pages." ;; *) echo "more information, such as the ld(1) and ld.so(8) manual pages." ;; esac echo "----------------------------------------------------------------------" fi exit $EXIT_SUCCESS } test finish = "$opt_mode" && func_mode_finish ${1+"$@"} # func_mode_install arg... func_mode_install () { $debug_cmd # There may be an optional sh(1) argument at the beginning of # install_prog (especially on Windows NT). if test "$SHELL" = "$nonopt" || test /bin/sh = "$nonopt" || # Allow the use of GNU shtool's install command. case $nonopt in *shtool*) :;; *) false;; esac then # Aesthetically quote it. func_quote_arg pretty "$nonopt" install_prog="$func_quote_arg_result " arg=$1 shift else install_prog= arg=$nonopt fi # The real first argument should be the name of the installation program. # Aesthetically quote it. func_quote_arg pretty "$arg" func_append install_prog "$func_quote_arg_result" install_shared_prog=$install_prog case " $install_prog " in *[\\\ /]cp\ *) install_cp=: ;; *) install_cp=false ;; esac # We need to accept at least all the BSD install flags. dest= files= opts= prev= install_type= isdir=false stripme= no_mode=: for arg do arg2= if test -n "$dest"; then func_append files " $dest" dest=$arg continue fi case $arg in -d) isdir=: ;; -f) if $install_cp; then :; else prev=$arg fi ;; -g | -m | -o) prev=$arg ;; -s) stripme=" -s" continue ;; -*) ;; *) # If the previous option needed an argument, then skip it. if test -n "$prev"; then if test X-m = "X$prev" && test -n "$install_override_mode"; then arg2=$install_override_mode no_mode=false fi prev= else dest=$arg continue fi ;; esac # Aesthetically quote the argument. func_quote_arg pretty "$arg" func_append install_prog " $func_quote_arg_result" if test -n "$arg2"; then func_quote_arg pretty "$arg2" fi func_append install_shared_prog " $func_quote_arg_result" done test -z "$install_prog" && \ func_fatal_help "you must specify an install program" test -n "$prev" && \ func_fatal_help "the '$prev' option requires an argument" if test -n "$install_override_mode" && $no_mode; then if $install_cp; then :; else func_quote_arg pretty "$install_override_mode" func_append install_shared_prog " -m $func_quote_arg_result" fi fi if test -z "$files"; then if test -z "$dest"; then func_fatal_help "no file or destination specified" else func_fatal_help "you must specify a destination" fi fi # Strip any trailing slash from the destination. func_stripname '' '/' "$dest" dest=$func_stripname_result # Check to see that the destination is a directory. test -d "$dest" && isdir=: if $isdir; then destdir=$dest destname= else func_dirname_and_basename "$dest" "" "." destdir=$func_dirname_result destname=$func_basename_result # Not a directory, so check to see that there is only one file specified. set dummy $files; shift test "$#" -gt 1 && \ func_fatal_help "'$dest' is not a directory" fi case $destdir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) for file in $files; do case $file in *.lo) ;; *) func_fatal_help "'$destdir' must be an absolute directory name" ;; esac done ;; esac # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic=$magic staticlibs= future_libdirs= current_libdirs= for file in $files; do # Do each installation. case $file in *.$libext) # Do the static libraries later. func_append staticlibs " $file" ;; *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "'$file' is not a valid libtool archive" library_names= old_library= relink_command= func_source "$file" # Add the libdir to current_libdirs if it is the destination. if test "X$destdir" = "X$libdir"; then case "$current_libdirs " in *" $libdir "*) ;; *) func_append current_libdirs " $libdir" ;; esac else # Note the libdir as a future libdir. case "$future_libdirs " in *" $libdir "*) ;; *) func_append future_libdirs " $libdir" ;; esac fi func_dirname "$file" "/" "" dir=$func_dirname_result func_append dir "$objdir" if test -n "$relink_command"; then # Determine the prefix the user has applied to our future dir. inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"` # Don't allow the user to place us outside of our expected # location b/c this prevents finding dependent libraries that # are installed to the same prefix. # At present, this check doesn't affect windows .dll's that # are installed into $libdir/../bin (currently, that works fine) # but it's something to keep an eye on. test "$inst_prefix_dir" = "$destdir" && \ func_fatal_error "error: cannot install '$file' to a directory not ending in $libdir" if test -n "$inst_prefix_dir"; then # Stick the inst_prefix_dir data into the link command. relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` else relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` fi func_warning "relinking '$file'" func_show_eval "$relink_command" \ 'func_fatal_error "error: relink '\''$file'\'' with the above command before installing it"' fi # See the names of the shared library. set dummy $library_names; shift if test -n "$1"; then realname=$1 shift srcname=$realname test -n "$relink_command" && srcname=${realname}T # Install the shared library and build the symlinks. func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ 'exit $?' tstripme=$stripme case $host_os in cygwin* | mingw* | pw32* | cegcc*) case $realname in *.dll.a) tstripme= ;; esac ;; os2*) case $realname in *_dll.a) tstripme= ;; esac ;; esac if test -n "$tstripme" && test -n "$striplib"; then func_show_eval "$striplib $destdir/$realname" 'exit $?' fi if test "$#" -gt 0; then # Delete the old symlinks, and create new ones. # Try 'ln -sf' first, because the 'ln' binary might depend on # the symlink we replace! Solaris /bin/ln does not understand -f, # so we also need to try rm && ln -s. for linkname do test "$linkname" != "$realname" \ && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" done fi # Do each command in the postinstall commands. lib=$destdir/$realname func_execute_cmds "$postinstall_cmds" 'exit $?' fi # Install the pseudo-library for information purposes. func_basename "$file" name=$func_basename_result instname=$dir/${name}i func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' # Maybe install the static library, too. test -n "$old_library" && func_append staticlibs " $dir/$old_library" ;; *.lo) # Install (i.e. copy) a libtool object. # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile=$destdir/$destname else func_basename "$file" destfile=$func_basename_result destfile=$destdir/$destfile fi # Deduce the name of the destination old-style object file. case $destfile in *.lo) func_lo2o "$destfile" staticdest=$func_lo2o_result ;; *.$objext) staticdest=$destfile destfile= ;; *) func_fatal_help "cannot copy a libtool object to '$destfile'" ;; esac # Install the libtool object if requested. test -n "$destfile" && \ func_show_eval "$install_prog $file $destfile" 'exit $?' # Install the old object if enabled. if test yes = "$build_old_libs"; then # Deduce the name of the old-style object file. func_lo2o "$file" staticobj=$func_lo2o_result func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' fi exit $EXIT_SUCCESS ;; *) # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile=$destdir/$destname else func_basename "$file" destfile=$func_basename_result destfile=$destdir/$destfile fi # If the file is missing, and there is a .exe on the end, strip it # because it is most likely a libtool script we actually want to # install stripped_ext= case $file in *.exe) if test ! -f "$file"; then func_stripname '' '.exe' "$file" file=$func_stripname_result stripped_ext=.exe fi ;; esac # Do a test to see if this is really a libtool program. case $host in *cygwin* | *mingw*) if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" wrapper=$func_ltwrapper_scriptname_result else func_stripname '' '.exe' "$file" wrapper=$func_stripname_result fi ;; *) wrapper=$file ;; esac if func_ltwrapper_script_p "$wrapper"; then notinst_deplibs= relink_command= func_source "$wrapper" # Check the variables that should have been set. test -z "$generated_by_libtool_version" && \ func_fatal_error "invalid libtool wrapper script '$wrapper'" finalize=: for lib in $notinst_deplibs; do # Check to see that each library is installed. libdir= if test -f "$lib"; then func_source "$lib" fi libfile=$libdir/`$ECHO "$lib" | $SED 's%^.*/%%g'` if test -n "$libdir" && test ! -f "$libfile"; then func_warning "'$lib' has not been installed in '$libdir'" finalize=false fi done relink_command= func_source "$wrapper" outputname= if test no = "$fast_install" && test -n "$relink_command"; then $opt_dry_run || { if $finalize; then tmpdir=`func_mktempdir` func_basename "$file$stripped_ext" file=$func_basename_result outputname=$tmpdir/$file # Replace the output file specification. relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` $opt_quiet || { func_quote_arg expand,pretty "$relink_command" eval "func_echo $func_quote_arg_result" } if eval "$relink_command"; then : else func_error "error: relink '$file' with the above command before installing it" $opt_dry_run || ${RM}r "$tmpdir" continue fi file=$outputname else func_warning "cannot relink '$file'" fi } else # Install the binary that we compiled earlier. file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` fi fi # remove .exe since cygwin /usr/bin/install will append another # one anyway case $install_prog,$host in */usr/bin/install*,*cygwin*) case $file:$destfile in *.exe:*.exe) # this is ok ;; *.exe:*) destfile=$destfile.exe ;; *:*.exe) func_stripname '' '.exe' "$destfile" destfile=$func_stripname_result ;; esac ;; esac func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' $opt_dry_run || if test -n "$outputname"; then ${RM}r "$tmpdir" fi ;; esac done for file in $staticlibs; do func_basename "$file" name=$func_basename_result # Set up the ranlib parameters. oldlib=$destdir/$name func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result func_show_eval "$install_prog \$file \$oldlib" 'exit $?' if test -n "$stripme" && test -n "$old_striplib"; then func_show_eval "$old_striplib $tool_oldlib" 'exit $?' fi # Do each command in the postinstall commands. func_execute_cmds "$old_postinstall_cmds" 'exit $?' done test -n "$future_libdirs" && \ func_warning "remember to run '$progname --finish$future_libdirs'" if test -n "$current_libdirs"; then # Maybe just do a dry run. $opt_dry_run && current_libdirs=" -n$current_libdirs" exec_cmd='$SHELL "$progpath" $preserve_args --finish$current_libdirs' else exit $EXIT_SUCCESS fi } test install = "$opt_mode" && func_mode_install ${1+"$@"} # func_generate_dlsyms outputname originator pic_p # Extract symbols from dlprefiles and create ${outputname}S.o with # a dlpreopen symbol table. func_generate_dlsyms () { $debug_cmd my_outputname=$1 my_originator=$2 my_pic_p=${3-false} my_prefix=`$ECHO "$my_originator" | $SED 's%[^a-zA-Z0-9]%_%g'` my_dlsyms= if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then if test -n "$NM" && test -n "$global_symbol_pipe"; then my_dlsyms=${my_outputname}S.c else func_error "not configured to extract global symbols from dlpreopened files" fi fi if test -n "$my_dlsyms"; then case $my_dlsyms in "") ;; *.c) # Discover the nlist of each of the dlfiles. nlist=$output_objdir/$my_outputname.nm func_show_eval "$RM $nlist ${nlist}S ${nlist}T" # Parse the name list into a source file. func_verbose "creating $output_objdir/$my_dlsyms" $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ /* $my_dlsyms - symbol resolution table for '$my_outputname' dlsym emulation. */ /* Generated by $PROGRAM (GNU $PACKAGE) $VERSION */ #ifdef __cplusplus extern \"C\" { #endif #if defined __GNUC__ && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) #pragma GCC diagnostic ignored \"-Wstrict-prototypes\" #endif /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif #define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) /* External symbol declarations for the compiler. */\ " if test yes = "$dlself"; then func_verbose "generating symbol list for '$output'" $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" # Add our own program objects to the symbol list. progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` for progfile in $progfiles; do func_to_tool_file "$progfile" func_convert_file_msys_to_w32 func_verbose "extracting global C symbols from '$func_to_tool_file_result'" $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" done if test -n "$exclude_expsyms"; then $opt_dry_run || { eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi if test -n "$export_symbols_regex"; then $opt_dry_run || { eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi # Prepare the list of exported symbols if test -z "$export_symbols"; then export_symbols=$output_objdir/$outputname.exp $opt_dry_run || { $RM $export_symbols eval "$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' ;; esac } else $opt_dry_run || { eval "$SED -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' ;; esac } fi fi for dlprefile in $dlprefiles; do func_verbose "extracting global C symbols from '$dlprefile'" func_basename "$dlprefile" name=$func_basename_result case $host in *cygwin* | *mingw* | *cegcc* ) # if an import library, we need to obtain dlname if func_win32_import_lib_p "$dlprefile"; then func_tr_sh "$dlprefile" eval "curr_lafile=\$libfile_$func_tr_sh_result" dlprefile_dlbasename= if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then # Use subshell, to avoid clobbering current variable values dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` if test -n "$dlprefile_dlname"; then func_basename "$dlprefile_dlname" dlprefile_dlbasename=$func_basename_result else # no lafile. user explicitly requested -dlpreopen . $sharedlib_from_linklib_cmd "$dlprefile" dlprefile_dlbasename=$sharedlib_from_linklib_result fi fi $opt_dry_run || { if test -n "$dlprefile_dlbasename"; then eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' else func_warning "Could not compute DLL name from $name" eval '$ECHO ": $name " >> "$nlist"' fi func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" } else # not an import lib $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } fi ;; *) $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } ;; esac done $opt_dry_run || { # Make sure we have at least an empty file. test -f "$nlist" || : > "$nlist" if test -n "$exclude_expsyms"; then $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T $MV "$nlist"T "$nlist" fi # Try sorting and uniquifying the output. if $GREP -v "^: " < "$nlist" | if sort -k 3 /dev/null 2>&1; then sort -k 3 else sort +2 fi | uniq > "$nlist"S; then : else $GREP -v "^: " < "$nlist" > "$nlist"S fi if test -f "$nlist"S; then eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' else echo '/* NONE */' >> "$output_objdir/$my_dlsyms" fi func_show_eval '$RM "${nlist}I"' if test -n "$global_symbol_to_import"; then eval "$global_symbol_to_import"' < "$nlist"S > "$nlist"I' fi echo >> "$output_objdir/$my_dlsyms" "\ /* The mapping between symbol names and symbols. */ typedef struct { const char *name; void *address; } lt_dlsymlist; extern LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[];\ " if test -s "$nlist"I; then echo >> "$output_objdir/$my_dlsyms" "\ static void lt_syminit(void) { LT_DLSYM_CONST lt_dlsymlist *symbol = lt_${my_prefix}_LTX_preloaded_symbols; for (; symbol->name; ++symbol) {" $SED 's/.*/ if (STREQ (symbol->name, \"&\")) symbol->address = (void *) \&&;/' < "$nlist"I >> "$output_objdir/$my_dlsyms" echo >> "$output_objdir/$my_dlsyms" "\ } }" fi echo >> "$output_objdir/$my_dlsyms" "\ LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[] = { {\"$my_originator\", (void *) 0}," if test -s "$nlist"I; then echo >> "$output_objdir/$my_dlsyms" "\ {\"@INIT@\", (void *) <_syminit}," fi case $need_lib_prefix in no) eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; *) eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; esac echo >> "$output_objdir/$my_dlsyms" "\ {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt_${my_prefix}_LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif\ " } # !$opt_dry_run pic_flag_for_symtable= case "$compile_command " in *" -static "*) ;; *) case $host in # compiling the symbol table file with pic_flag works around # a FreeBSD bug that causes programs to crash when -lm is # linked before any other PIC object. But we must not use # pic_flag when linking with -static. The problem exists in # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; *-*-hpux*) pic_flag_for_symtable=" $pic_flag" ;; *) $my_pic_p && pic_flag_for_symtable=" $pic_flag" ;; esac ;; esac symtab_cflags= for arg in $LTCFLAGS; do case $arg in -pie | -fpie | -fPIE) ;; *) func_append symtab_cflags " $arg" ;; esac done # Now compile the dynamic symbol file. func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' # Clean up the generated files. func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T" "${nlist}I"' # Transform the symbol file into the correct name. symfileobj=$output_objdir/${my_outputname}S.$objext case $host in *cygwin* | *mingw* | *cegcc* ) if test -f "$output_objdir/$my_outputname.def"; then compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` else compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` fi ;; *) compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` ;; esac ;; *) func_fatal_error "unknown suffix for '$my_dlsyms'" ;; esac else # We keep going just in case the user didn't refer to # lt_preloaded_symbols. The linker will fail if global_symbol_pipe # really was required. # Nullify the symbol file. compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"` finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` fi } # func_cygming_gnu_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is a GNU/binutils-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_gnu_implib_p () { $debug_cmd func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` test -n "$func_cygming_gnu_implib_tmp" } # func_cygming_ms_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is an MS-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_ms_implib_p () { $debug_cmd func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` test -n "$func_cygming_ms_implib_tmp" } # func_win32_libid arg # return the library type of file 'arg' # # Need a lot of goo to handle *both* DLLs and import libs # Has to be a shell function in order to 'eat' the argument # that is supplied when $file_magic_command is called. # Despite the name, also deal with 64 bit binaries. func_win32_libid () { $debug_cmd win32_libid_type=unknown win32_fileres=`file -L $1 2>/dev/null` case $win32_fileres in *ar\ archive\ import\ library*) # definitely import win32_libid_type="x86 archive import" ;; *ar\ archive*) # could be an import, or static # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then case $nm_interface in "MS dumpbin") if func_cygming_ms_implib_p "$1" || func_cygming_gnu_implib_p "$1" then win32_nmres=import else win32_nmres= fi ;; *) func_to_tool_file "$1" func_convert_file_msys_to_w32 win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | $SED -n -e ' 1,100{ / I /{ s|.*|import| p q } }'` ;; esac case $win32_nmres in import*) win32_libid_type="x86 archive import";; *) win32_libid_type="x86 archive static";; esac fi ;; *DLL*) win32_libid_type="x86 DLL" ;; *executable*) # but shell scripts are "executable" too... case $win32_fileres in *MS\ Windows\ PE\ Intel*) win32_libid_type="x86 DLL" ;; esac ;; esac $ECHO "$win32_libid_type" } # func_cygming_dll_for_implib ARG # # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib () { $debug_cmd sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` } # func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs # # The is the core of a fallback implementation of a # platform-specific function to extract the name of the # DLL associated with the specified import library LIBNAME. # # SECTION_NAME is either .idata$6 or .idata$7, depending # on the platform and compiler that created the implib. # # Echos the name of the DLL associated with the # specified import library. func_cygming_dll_for_implib_fallback_core () { $debug_cmd match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` $OBJDUMP -s --section "$1" "$2" 2>/dev/null | $SED '/^Contents of section '"$match_literal"':/{ # Place marker at beginning of archive member dllname section s/.*/====MARK====/ p d } # These lines can sometimes be longer than 43 characters, but # are always uninteresting /:[ ]*file format pe[i]\{,1\}-/d /^In archive [^:]*:/d # Ensure marker is printed /^====MARK====/p # Remove all lines with less than 43 characters /^.\{43\}/!d # From remaining lines, remove first 43 characters s/^.\{43\}//' | $SED -n ' # Join marker and all lines until next marker into a single line /^====MARK====/ b para H $ b para b :para x s/\n//g # Remove the marker s/^====MARK====// # Remove trailing dots and whitespace s/[\. \t]*$// # Print /./p' | # we now have a list, one entry per line, of the stringified # contents of the appropriate section of all members of the # archive that possess that section. Heuristic: eliminate # all those that have a first or second character that is # a '.' (that is, objdump's representation of an unprintable # character.) This should work for all archives with less than # 0x302f exports -- but will fail for DLLs whose name actually # begins with a literal '.' or a single character followed by # a '.'. # # Of those that remain, print the first one. $SED -e '/^\./d;/^.\./d;q' } # func_cygming_dll_for_implib_fallback ARG # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # # This fallback implementation is for use when $DLLTOOL # does not support the --identify-strict option. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib_fallback () { $debug_cmd if func_cygming_gnu_implib_p "$1"; then # binutils import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` elif func_cygming_ms_implib_p "$1"; then # ms-generated import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` else # unknown sharedlib_from_linklib_result= fi } # func_extract_an_archive dir oldlib func_extract_an_archive () { $debug_cmd f_ex_an_ar_dir=$1; shift f_ex_an_ar_oldlib=$1 if test yes = "$lock_old_archive_extraction"; then lockfile=$f_ex_an_ar_oldlib.lock until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done fi func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ 'stat=$?; rm -f "$lockfile"; exit $stat' if test yes = "$lock_old_archive_extraction"; then $opt_dry_run || rm -f "$lockfile" fi if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then : else func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" fi } # func_extract_archives gentop oldlib ... func_extract_archives () { $debug_cmd my_gentop=$1; shift my_oldlibs=${1+"$@"} my_oldobjs= my_xlib= my_xabs= my_xdir= for my_xlib in $my_oldlibs; do # Extract the objects. case $my_xlib in [\\/]* | [A-Za-z]:[\\/]*) my_xabs=$my_xlib ;; *) my_xabs=`pwd`"/$my_xlib" ;; esac func_basename "$my_xlib" my_xlib=$func_basename_result my_xlib_u=$my_xlib while :; do case " $extracted_archives " in *" $my_xlib_u "*) func_arith $extracted_serial + 1 extracted_serial=$func_arith_result my_xlib_u=lt$extracted_serial-$my_xlib ;; *) break ;; esac done extracted_archives="$extracted_archives $my_xlib_u" my_xdir=$my_gentop/$my_xlib_u func_mkdir_p "$my_xdir" case $host in *-darwin*) func_verbose "Extracting $my_xabs" # Do not bother doing anything if just a dry run $opt_dry_run || { darwin_orig_dir=`pwd` cd $my_xdir || exit $? darwin_archive=$my_xabs darwin_curdir=`pwd` func_basename "$darwin_archive" darwin_base_archive=$func_basename_result darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` if test -n "$darwin_arches"; then darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` darwin_arch= func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" for darwin_arch in $darwin_arches; do func_mkdir_p "unfat-$$/$darwin_base_archive-$darwin_arch" $LIPO -thin $darwin_arch -output "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" "$darwin_archive" cd "unfat-$$/$darwin_base_archive-$darwin_arch" func_extract_an_archive "`pwd`" "$darwin_base_archive" cd "$darwin_curdir" $RM "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" done # $darwin_arches ## Okay now we've a bunch of thin objects, gotta fatten them up :) darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$sed_basename" | sort -u` darwin_file= darwin_files= for darwin_file in $darwin_filelist; do darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` $LIPO -create -output "$darwin_file" $darwin_files done # $darwin_filelist $RM -rf unfat-$$ cd "$darwin_orig_dir" else cd $darwin_orig_dir func_extract_an_archive "$my_xdir" "$my_xabs" fi # $darwin_arches } # !$opt_dry_run ;; *) func_extract_an_archive "$my_xdir" "$my_xabs" ;; esac my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` done func_extract_archives_result=$my_oldobjs } # func_emit_wrapper [arg=no] # # Emit a libtool wrapper script on stdout. # Don't directly open a file because we may want to # incorporate the script contents within a cygwin/mingw # wrapper executable. Must ONLY be called from within # func_mode_link because it depends on a number of variables # set therein. # # ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR # variable will take. If 'yes', then the emitted script # will assume that the directory where it is stored is # the $objdir directory. This is a cygwin/mingw-specific # behavior. func_emit_wrapper () { func_emit_wrapper_arg1=${1-no} $ECHO "\ #! $SHELL # $output - temporary wrapper script for $objdir/$outputname # Generated by $PROGRAM (GNU $PACKAGE) $VERSION # # The $output program cannot be directly executed until all the libtool # libraries that it depends on are installed. # # This wrapper script should never be moved out of the build directory. # If it is, it will not operate correctly. # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='$sed_quote_subst' # Be Bourne compatible if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH relink_command=\"$relink_command\" # This environment variable determines our operation mode. if test \"\$libtool_install_magic\" = \"$magic\"; then # install mode needs the following variables: generated_by_libtool_version='$macro_version' notinst_deplibs='$notinst_deplibs' else # When we are sourced in execute mode, \$file and \$ECHO are already set. if test \"\$libtool_execute_magic\" != \"$magic\"; then file=\"\$0\"" func_quote_arg pretty "$ECHO" qECHO=$func_quote_arg_result $ECHO "\ # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } ECHO=$qECHO fi # Very basic option parsing. These options are (a) specific to # the libtool wrapper, (b) are identical between the wrapper # /script/ and the wrapper /executable/ that is used only on # windows platforms, and (c) all begin with the string "--lt-" # (application programs are unlikely to have options that match # this pattern). # # There are only two supported options: --lt-debug and # --lt-dump-script. There is, deliberately, no --lt-help. # # The first argument to this parsing function should be the # script's $0 value, followed by "$@". lt_option_debug= func_parse_lt_options () { lt_script_arg0=\$0 shift for lt_opt do case \"\$lt_opt\" in --lt-debug) lt_option_debug=1 ;; --lt-dump-script) lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` cat \"\$lt_dump_D/\$lt_dump_F\" exit 0 ;; --lt-*) \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 exit 1 ;; esac done # Print the debug banner immediately: if test -n \"\$lt_option_debug\"; then echo \"$outputname:$output:\$LINENO: libtool wrapper (GNU $PACKAGE) $VERSION\" 1>&2 fi } # Used when --lt-debug. Prints its arguments to stdout # (redirection is the responsibility of the caller) func_lt_dump_args () { lt_dump_args_N=1; for lt_arg do \$ECHO \"$outputname:$output:\$LINENO: newargv[\$lt_dump_args_N]: \$lt_arg\" lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` done } # Core function for launching the target application func_exec_program_core () { " case $host in # Backslashes separate directories on plain windows *-*-mingw | *-*-os2* | *-cegcc*) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir\\\\\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} " ;; *) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir/\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir/\$program\" \${1+\"\$@\"} " ;; esac $ECHO "\ \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 exit 1 } # A function to encapsulate launching the target application # Strips options in the --lt-* namespace from \$@ and # launches target application with the remaining arguments. func_exec_program () { case \" \$* \" in *\\ --lt-*) for lt_wr_arg do case \$lt_wr_arg in --lt-*) ;; *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; esac shift done ;; esac func_exec_program_core \${1+\"\$@\"} } # Parse options func_parse_lt_options \"\$0\" \${1+\"\$@\"} # Find the directory that this script lives in. thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\` test \"x\$thisdir\" = \"x\$file\" && thisdir=. # Follow symbolic links until we get to the real thisdir. file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\` while test -n \"\$file\"; do destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\` # If there was a directory component, then change thisdir. if test \"x\$destdir\" != \"x\$file\"; then case \"\$destdir\" in [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; *) thisdir=\"\$thisdir/\$destdir\" ;; esac fi file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\` file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` done # Usually 'no', except on cygwin/mingw when embedded into # the cwrapper. WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then # special case for '.' if test \"\$thisdir\" = \".\"; then thisdir=\`pwd\` fi # remove .libs from thisdir case \"\$thisdir\" in *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; $objdir ) thisdir=. ;; esac fi # Try to get the absolute directory name. absdir=\`cd \"\$thisdir\" && pwd\` test -n \"\$absdir\" && thisdir=\"\$absdir\" " if test yes = "$fast_install"; then $ECHO "\ program=lt-'$outputname'$exeext progdir=\"\$thisdir/$objdir\" if test ! -f \"\$progdir/\$program\" || { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | $SED 1q\`; \\ test \"X\$file\" != \"X\$progdir/\$program\"; }; then file=\"\$\$-\$program\" if test ! -d \"\$progdir\"; then $MKDIR \"\$progdir\" else $RM \"\$progdir/\$file\" fi" $ECHO "\ # relink executable if necessary if test -n \"\$relink_command\"; then if relink_command_output=\`eval \$relink_command 2>&1\`; then : else \$ECHO \"\$relink_command_output\" >&2 $RM \"\$progdir/\$file\" exit 1 fi fi $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || { $RM \"\$progdir/\$program\"; $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } $RM \"\$progdir/\$file\" fi" else $ECHO "\ program='$outputname' progdir=\"\$thisdir/$objdir\" " fi $ECHO "\ if test -f \"\$progdir/\$program\"; then" # fixup the dll searchpath if we need to. # # Fix the DLL searchpath if we need to. Do this before prepending # to shlibpath, because on Windows, both are PATH and uninstalled # libraries must come first. if test -n "$dllsearchpath"; then $ECHO "\ # Add the dll search path components to the executable PATH PATH=$dllsearchpath:\$PATH " fi # Export our shlibpath_var if we have one. if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then $ECHO "\ # Add our own library path to $shlibpath_var $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" # Some systems cannot cope with colon-terminated $shlibpath_var # The second colon is a workaround for a bug in BeOS R4 sed $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\` export $shlibpath_var " fi $ECHO "\ if test \"\$libtool_execute_magic\" != \"$magic\"; then # Run the actual program with our arguments. func_exec_program \${1+\"\$@\"} fi else # The program doesn't exist. \$ECHO \"\$0: error: '\$progdir/\$program' does not exist\" 1>&2 \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 exit 1 fi fi\ " } # func_emit_cwrapperexe_src # emit the source code for a wrapper executable on stdout # Must ONLY be called from within func_mode_link because # it depends on a number of variable set therein. func_emit_cwrapperexe_src () { cat < #include #ifdef _MSC_VER # include # include # include #else # include # include # ifdef __CYGWIN__ # include # endif #endif #include #include #include #include #include #include #include #include #define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) /* declarations of non-ANSI functions */ #if defined __MINGW32__ # ifdef __STRICT_ANSI__ int _putenv (const char *); # endif #elif defined __CYGWIN__ # ifdef __STRICT_ANSI__ char *realpath (const char *, char *); int putenv (char *); int setenv (const char *, const char *, int); # endif /* #elif defined other_platform || defined ... */ #endif /* portability defines, excluding path handling macros */ #if defined _MSC_VER # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv # define S_IXUSR _S_IEXEC #elif defined __MINGW32__ # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv #elif defined __CYGWIN__ # define HAVE_SETENV # define FOPEN_WB "wb" /* #elif defined other platforms ... */ #endif #if defined PATH_MAX # define LT_PATHMAX PATH_MAX #elif defined MAXPATHLEN # define LT_PATHMAX MAXPATHLEN #else # define LT_PATHMAX 1024 #endif #ifndef S_IXOTH # define S_IXOTH 0 #endif #ifndef S_IXGRP # define S_IXGRP 0 #endif /* path handling portability macros */ #ifndef DIR_SEPARATOR # define DIR_SEPARATOR '/' # define PATH_SEPARATOR ':' #endif #if defined _WIN32 || defined __MSDOS__ || defined __DJGPP__ || \ defined __OS2__ # define HAVE_DOS_BASED_FILE_SYSTEM # define FOPEN_WB "wb" # ifndef DIR_SEPARATOR_2 # define DIR_SEPARATOR_2 '\\' # endif # ifndef PATH_SEPARATOR_2 # define PATH_SEPARATOR_2 ';' # endif #endif #ifndef DIR_SEPARATOR_2 # define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) #else /* DIR_SEPARATOR_2 */ # define IS_DIR_SEPARATOR(ch) \ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) #endif /* DIR_SEPARATOR_2 */ #ifndef PATH_SEPARATOR_2 # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) #else /* PATH_SEPARATOR_2 */ # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) #endif /* PATH_SEPARATOR_2 */ #ifndef FOPEN_WB # define FOPEN_WB "w" #endif #ifndef _O_BINARY # define _O_BINARY 0 #endif #define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) #define XFREE(stale) do { \ if (stale) { free (stale); stale = 0; } \ } while (0) #if defined LT_DEBUGWRAPPER static int lt_debug = 1; #else static int lt_debug = 0; #endif const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ void *xmalloc (size_t num); char *xstrdup (const char *string); const char *base_name (const char *name); char *find_executable (const char *wrapper); char *chase_symlinks (const char *pathspec); int make_executable (const char *path); int check_executable (const char *path); char *strendzap (char *str, const char *pat); void lt_debugprintf (const char *file, int line, const char *fmt, ...); void lt_fatal (const char *file, int line, const char *message, ...); static const char *nonnull (const char *s); static const char *nonempty (const char *s); void lt_setenv (const char *name, const char *value); char *lt_extend_str (const char *orig_value, const char *add, int to_end); void lt_update_exe_path (const char *name, const char *value); void lt_update_lib_path (const char *name, const char *value); char **prepare_spawn (char **argv); void lt_dump_script (FILE *f); EOF cat <= 0) && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return 1; else return 0; } int make_executable (const char *path) { int rval = 0; struct stat st; lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", nonempty (path)); if ((!path) || (!*path)) return 0; if (stat (path, &st) >= 0) { rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); } return rval; } /* Searches for the full path of the wrapper. Returns newly allocated full path name if found, NULL otherwise Does not chase symlinks, even on platforms that support them. */ char * find_executable (const char *wrapper) { int has_slash = 0; const char *p; const char *p_next; /* static buffer for getcwd */ char tmp[LT_PATHMAX + 1]; size_t tmp_len; char *concat_name; lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", nonempty (wrapper)); if ((wrapper == NULL) || (*wrapper == '\0')) return NULL; /* Absolute path? */ #if defined HAVE_DOS_BASED_FILE_SYSTEM if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } else { #endif if (IS_DIR_SEPARATOR (wrapper[0])) { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } #if defined HAVE_DOS_BASED_FILE_SYSTEM } #endif for (p = wrapper; *p; p++) if (*p == '/') { has_slash = 1; break; } if (!has_slash) { /* no slashes; search PATH */ const char *path = getenv ("PATH"); if (path != NULL) { for (p = path; *p; p = p_next) { const char *q; size_t p_len; for (q = p; *q; q++) if (IS_PATH_SEPARATOR (*q)) break; p_len = (size_t) (q - p); p_next = (*q == '\0' ? q : q + 1); if (p_len == 0) { /* empty path: current directory */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); } else { concat_name = XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, p, p_len); concat_name[p_len] = '/'; strcpy (concat_name + p_len + 1, wrapper); } if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } } /* not found in PATH; assume curdir */ } /* Relative path | not found in path: prepend cwd */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); return NULL; } char * chase_symlinks (const char *pathspec) { #ifndef S_ISLNK return xstrdup (pathspec); #else char buf[LT_PATHMAX]; struct stat s; char *tmp_pathspec = xstrdup (pathspec); char *p; int has_symlinks = 0; while (strlen (tmp_pathspec) && !has_symlinks) { lt_debugprintf (__FILE__, __LINE__, "checking path component for symlinks: %s\n", tmp_pathspec); if (lstat (tmp_pathspec, &s) == 0) { if (S_ISLNK (s.st_mode) != 0) { has_symlinks = 1; break; } /* search backwards for last DIR_SEPARATOR */ p = tmp_pathspec + strlen (tmp_pathspec) - 1; while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) p--; if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) { /* no more DIR_SEPARATORS left */ break; } *p = '\0'; } else { lt_fatal (__FILE__, __LINE__, "error accessing file \"%s\": %s", tmp_pathspec, nonnull (strerror (errno))); } } XFREE (tmp_pathspec); if (!has_symlinks) { return xstrdup (pathspec); } tmp_pathspec = realpath (pathspec, buf); if (tmp_pathspec == 0) { lt_fatal (__FILE__, __LINE__, "could not follow symlinks for %s", pathspec); } return xstrdup (tmp_pathspec); #endif } char * strendzap (char *str, const char *pat) { size_t len, patlen; assert (str != NULL); assert (pat != NULL); len = strlen (str); patlen = strlen (pat); if (patlen <= len) { str += len - patlen; if (STREQ (str, pat)) *str = '\0'; } return str; } void lt_debugprintf (const char *file, int line, const char *fmt, ...) { va_list args; if (lt_debug) { (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); va_start (args, fmt); (void) vfprintf (stderr, fmt, args); va_end (args); } } static void lt_error_core (int exit_status, const char *file, int line, const char *mode, const char *message, va_list ap) { fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); vfprintf (stderr, message, ap); fprintf (stderr, ".\n"); if (exit_status >= 0) exit (exit_status); } void lt_fatal (const char *file, int line, const char *message, ...) { va_list ap; va_start (ap, message); lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); va_end (ap); } static const char * nonnull (const char *s) { return s ? s : "(null)"; } static const char * nonempty (const char *s) { return (s && !*s) ? "(empty)" : nonnull (s); } void lt_setenv (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_setenv) setting '%s' to '%s'\n", nonnull (name), nonnull (value)); { #ifdef HAVE_SETENV /* always make a copy, for consistency with !HAVE_SETENV */ char *str = xstrdup (value); setenv (name, str, 1); #else size_t len = strlen (name) + 1 + strlen (value) + 1; char *str = XMALLOC (char, len); sprintf (str, "%s=%s", name, value); if (putenv (str) != EXIT_SUCCESS) { XFREE (str); } #endif } } char * lt_extend_str (const char *orig_value, const char *add, int to_end) { char *new_value; if (orig_value && *orig_value) { size_t orig_value_len = strlen (orig_value); size_t add_len = strlen (add); new_value = XMALLOC (char, add_len + orig_value_len + 1); if (to_end) { strcpy (new_value, orig_value); strcpy (new_value + orig_value_len, add); } else { strcpy (new_value, add); strcpy (new_value + add_len, orig_value); } } else { new_value = xstrdup (add); } return new_value; } void lt_update_exe_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); /* some systems can't cope with a ':'-terminated path #' */ size_t len = strlen (new_value); while ((len > 0) && IS_PATH_SEPARATOR (new_value[len-1])) { new_value[--len] = '\0'; } lt_setenv (name, new_value); XFREE (new_value); } } void lt_update_lib_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); lt_setenv (name, new_value); XFREE (new_value); } } EOF case $host_os in mingw*) cat <<"EOF" /* Prepares an argument vector before calling spawn(). Note that spawn() does not by itself call the command interpreter (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&v); v.dwPlatformId == VER_PLATFORM_WIN32_NT; }) ? "cmd.exe" : "command.com"). Instead it simply concatenates the arguments, separated by ' ', and calls CreateProcess(). We must quote the arguments since Win32 CreateProcess() interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a special way: - Space and tab are interpreted as delimiters. They are not treated as delimiters if they are surrounded by double quotes: "...". - Unescaped double quotes are removed from the input. Their only effect is that within double quotes, space and tab are treated like normal characters. - Backslashes not followed by double quotes are not special. - But 2*n+1 backslashes followed by a double quote become n backslashes followed by a double quote (n >= 0): \" -> " \\\" -> \" \\\\\" -> \\" */ #define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" #define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" char ** prepare_spawn (char **argv) { size_t argc; char **new_argv; size_t i; /* Count number of arguments. */ for (argc = 0; argv[argc] != NULL; argc++) ; /* Allocate new argument vector. */ new_argv = XMALLOC (char *, argc + 1); /* Put quoted arguments into the new argument vector. */ for (i = 0; i < argc; i++) { const char *string = argv[i]; if (string[0] == '\0') new_argv[i] = xstrdup ("\"\""); else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) { int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); size_t length; unsigned int backslashes; const char *s; char *quoted_string; char *p; length = 0; backslashes = 0; if (quote_around) length++; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') length += backslashes + 1; length++; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) length += backslashes + 1; quoted_string = XMALLOC (char, length + 1); p = quoted_string; backslashes = 0; if (quote_around) *p++ = '"'; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') { unsigned int j; for (j = backslashes + 1; j > 0; j--) *p++ = '\\'; } *p++ = c; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) { unsigned int j; for (j = backslashes; j > 0; j--) *p++ = '\\'; *p++ = '"'; } *p = '\0'; new_argv[i] = quoted_string; } else new_argv[i] = (char *) string; } new_argv[argc] = NULL; return new_argv; } EOF ;; esac cat <<"EOF" void lt_dump_script (FILE* f) { EOF func_emit_wrapper yes | $SED -n -e ' s/^\(.\{79\}\)\(..*\)/\1\ \2/ h s/\([\\"]\)/\\\1/g s/$/\\n/ s/\([^\n]*\).*/ fputs ("\1", f);/p g D' cat <<"EOF" } EOF } # end: func_emit_cwrapperexe_src # func_win32_import_lib_p ARG # True if ARG is an import lib, as indicated by $file_magic_cmd func_win32_import_lib_p () { $debug_cmd case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in *import*) : ;; *) false ;; esac } # func_suncc_cstd_abi # !!ONLY CALL THIS FOR SUN CC AFTER $compile_command IS FULLY EXPANDED!! # Several compiler flags select an ABI that is incompatible with the # Cstd library. Avoid specifying it if any are in CXXFLAGS. func_suncc_cstd_abi () { $debug_cmd case " $compile_command " in *" -compat=g "*|*\ -std=c++[0-9][0-9]\ *|*" -library=stdcxx4 "*|*" -library=stlport4 "*) suncc_use_cstd_abi=no ;; *) suncc_use_cstd_abi=yes ;; esac } # func_mode_link arg... func_mode_link () { $debug_cmd case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) # It is impossible to link a dll without this setting, and # we shouldn't force the makefile maintainer to figure out # what system we are compiling for in order to pass an extra # flag for every libtool invocation. # allow_undefined=no # FIXME: Unfortunately, there are problems with the above when trying # to make a dll that has undefined symbols, in which case not # even a static library is built. For now, we need to specify # -no-undefined on the libtool link line when we can be certain # that all symbols are satisfied, otherwise we get a static library. allow_undefined=yes ;; *) allow_undefined=yes ;; esac libtool_args=$nonopt base_compile="$nonopt $@" compile_command=$nonopt finalize_command=$nonopt compile_rpath= finalize_rpath= compile_shlibpath= finalize_shlibpath= convenience= old_convenience= deplibs= old_deplibs= compiler_flags= linker_flags= dllsearchpath= lib_search_path=`pwd` inst_prefix_dir= new_inherited_linker_flags= avoid_version=no bindir= dlfiles= dlprefiles= dlself=no export_dynamic=no export_symbols= export_symbols_regex= generated= libobjs= ltlibs= module=no no_install=no objs= os2dllname= non_pic_objects= precious_files_regex= prefer_static_libs=no preload=false prev= prevarg= release= rpath= xrpath= perm_rpath= temp_rpath= thread_safe=no vinfo= vinfo_number=no weak_libs= single_module=$wl-single_module func_infer_tag $base_compile # We need to know -static, to get the right output filenames. for arg do case $arg in -shared) test yes != "$build_libtool_libs" \ && func_fatal_configuration "cannot build a shared library" build_old_libs=no break ;; -all-static | -static | -static-libtool-libs) case $arg in -all-static) if test yes = "$build_libtool_libs" && test -z "$link_static_flag"; then func_warning "complete static linking is impossible in this configuration" fi if test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; -static) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=built ;; -static-libtool-libs) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; esac build_libtool_libs=no build_old_libs=yes break ;; esac done # See if our shared archives depend on static archives. test -n "$old_archive_from_new_cmds" && build_old_libs=yes # Go through the arguments, transforming them on the way. while test "$#" -gt 0; do arg=$1 shift func_quote_arg pretty,unquoted "$arg" qarg=$func_quote_arg_unquoted_result func_append libtool_args " $func_quote_arg_result" # If the previous option needs an argument, assign it. if test -n "$prev"; then case $prev in output) func_append compile_command " @OUTPUT@" func_append finalize_command " @OUTPUT@" ;; esac case $prev in bindir) bindir=$arg prev= continue ;; dlfiles|dlprefiles) $preload || { # Add the symbol object into the linking commands. func_append compile_command " @SYMFILE@" func_append finalize_command " @SYMFILE@" preload=: } case $arg in *.la | *.lo) ;; # We handle these cases below. force) if test no = "$dlself"; then dlself=needless export_dynamic=yes fi prev= continue ;; self) if test dlprefiles = "$prev"; then dlself=yes elif test dlfiles = "$prev" && test yes != "$dlopen_self"; then dlself=yes else dlself=needless export_dynamic=yes fi prev= continue ;; *) if test dlfiles = "$prev"; then func_append dlfiles " $arg" else func_append dlprefiles " $arg" fi prev= continue ;; esac ;; expsyms) export_symbols=$arg test -f "$arg" \ || func_fatal_error "symbol file '$arg' does not exist" prev= continue ;; expsyms_regex) export_symbols_regex=$arg prev= continue ;; framework) case $host in *-*-darwin*) case "$deplibs " in *" $qarg.ltframework "*) ;; *) func_append deplibs " $qarg.ltframework" # this is fixed later ;; esac ;; esac prev= continue ;; inst_prefix) inst_prefix_dir=$arg prev= continue ;; mllvm) # Clang does not use LLVM to link, so we can simply discard any # '-mllvm $arg' options when doing the link step. prev= continue ;; objectlist) if test -f "$arg"; then save_arg=$arg moreargs= for fil in `cat "$save_arg"` do # func_append moreargs " $fil" arg=$fil # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test none = "$pic_object" && test none = "$non_pic_object"; then func_fatal_error "cannot find name of object for '$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result if test none != "$pic_object"; then # Prepend the subdirectory the object is found in. pic_object=$xdir$pic_object if test dlfiles = "$prev"; then if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test dlprefiles = "$prev"; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg=$pic_object fi # Non-PIC object. if test none != "$non_pic_object"; then # Prepend the subdirectory the object is found in. non_pic_object=$xdir$non_pic_object # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test none = "$pic_object"; then arg=$non_pic_object fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object=$pic_object func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "'$arg' is not a valid libtool object" fi fi done else func_fatal_error "link input file '$arg' does not exist" fi arg=$save_arg prev= continue ;; os2dllname) os2dllname=$arg prev= continue ;; precious_regex) precious_files_regex=$arg prev= continue ;; release) release=-$arg prev= continue ;; rpath | xrpath) # We need an absolute path. case $arg in [\\/]* | [A-Za-z]:[\\/]*) ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac if test rpath = "$prev"; then case "$rpath " in *" $arg "*) ;; *) func_append rpath " $arg" ;; esac else case "$xrpath " in *" $arg "*) ;; *) func_append xrpath " $arg" ;; esac fi prev= continue ;; shrext) shrext_cmds=$arg prev= continue ;; weak) func_append weak_libs " $arg" prev= continue ;; xcclinker) func_append linker_flags " $qarg" func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xcompiler) func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xlinker) func_append linker_flags " $qarg" func_append compiler_flags " $wl$qarg" prev= func_append compile_command " $wl$qarg" func_append finalize_command " $wl$qarg" continue ;; *) eval "$prev=\"\$arg\"" prev= continue ;; esac fi # test -n "$prev" prevarg=$arg case $arg in -all-static) if test -n "$link_static_flag"; then # See comment for -static flag below, for more details. func_append compile_command " $link_static_flag" func_append finalize_command " $link_static_flag" fi continue ;; -allow-undefined) # FIXME: remove this flag sometime in the future. func_fatal_error "'-allow-undefined' must not be used because it is the default" ;; -avoid-version) avoid_version=yes continue ;; -bindir) prev=bindir continue ;; -dlopen) prev=dlfiles continue ;; -dlpreopen) prev=dlprefiles continue ;; -export-dynamic) export_dynamic=yes continue ;; -export-symbols | -export-symbols-regex) if test -n "$export_symbols" || test -n "$export_symbols_regex"; then func_fatal_error "more than one -exported-symbols argument is not allowed" fi if test X-export-symbols = "X$arg"; then prev=expsyms else prev=expsyms_regex fi continue ;; -framework) prev=framework continue ;; -inst-prefix-dir) prev=inst_prefix continue ;; # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* # so, if we see these flags be careful not to treat them like -L -L[A-Z][A-Z]*:*) case $with_gcc/$host in no/*-*-irix* | /*-*-irix*) func_append compile_command " $arg" func_append finalize_command " $arg" ;; esac continue ;; -L*) func_stripname "-L" '' "$arg" if test -z "$func_stripname_result"; then if test "$#" -gt 0; then func_fatal_error "require no space between '-L' and '$1'" else func_fatal_error "need path for '-L' option" fi fi func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) absdir=`cd "$dir" && pwd` test -z "$absdir" && \ func_fatal_error "cannot determine absolute directory name of '$dir'" dir=$absdir ;; esac case "$deplibs " in *" -L$dir "* | *" $arg "*) # Will only happen for absolute or sysroot arguments ;; *) # Preserve sysroot, but never include relative directories case $dir in [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; *) func_append deplibs " -L$dir" ;; esac func_append lib_search_path " $dir" ;; esac case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` case :$dllsearchpath: in *":$dir:"*) ;; ::) dllsearchpath=$dir;; *) func_append dllsearchpath ":$dir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac continue ;; -l*) if test X-lc = "X$arg" || test X-lm = "X$arg"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) # These systems don't actually have a C or math library (as such) continue ;; *-*-os2*) # These systems don't actually have a C library (as such) test X-lc = "X$arg" && continue ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) # Do not include libc due to us having libc/libc_r. test X-lc = "X$arg" && continue ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C and math libraries are in the System framework func_append deplibs " System.ltframework" continue ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype test X-lc = "X$arg" && continue ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work test X-lc = "X$arg" && continue ;; esac elif test X-lc_r = "X$arg"; then case $host in *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) # Do not include libc_r directly, use -pthread flag. continue ;; esac fi func_append deplibs " $arg" continue ;; -mllvm) prev=mllvm continue ;; -module) module=yes continue ;; # Tru64 UNIX uses -model [arg] to determine the layout of C++ # classes, name mangling, and exception handling. # Darwin uses the -arch flag to determine output architecture. -model|-arch|-isysroot|--sysroot) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" prev=xcompiler continue ;; -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" case "$new_inherited_linker_flags " in *" $arg "*) ;; * ) func_append new_inherited_linker_flags " $arg" ;; esac continue ;; -multi_module) single_module=$wl-multi_module continue ;; -no-fast-install) fast_install=no continue ;; -no-install) case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) # The PATH hackery in wrapper scripts is required on Windows # and Darwin in order for the loader to find any dlls it needs. func_warning "'-no-install' is ignored for $host" func_warning "assuming '-no-fast-install' instead" fast_install=no ;; *) no_install=yes ;; esac continue ;; -no-undefined) allow_undefined=no continue ;; -objectlist) prev=objectlist continue ;; -os2dllname) prev=os2dllname continue ;; -o) prev=output ;; -precious-files-regex) prev=precious_regex continue ;; -release) prev=release continue ;; -rpath) prev=rpath continue ;; -R) prev=xrpath continue ;; -R*) func_stripname '-R' '' "$arg" dir=$func_stripname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; =*) func_stripname '=' '' "$dir" dir=$lt_sysroot$func_stripname_result ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac continue ;; -shared) # The effects of -shared are defined in a previous loop. continue ;; -shrext) prev=shrext continue ;; -static | -static-libtool-libs) # The effects of -static are defined in a previous loop. # We used to do the same as -all-static on platforms that # didn't have a PIC flag, but the assumption that the effects # would be equivalent was wrong. It would break on at least # Digital Unix and AIX. continue ;; -thread-safe) thread_safe=yes continue ;; -version-info) prev=vinfo continue ;; -version-number) prev=vinfo vinfo_number=yes continue ;; -weak) prev=weak continue ;; -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result arg= save_ifs=$IFS; IFS=, for flag in $args; do IFS=$save_ifs func_quote_arg pretty "$flag" func_append arg " $func_quote_arg_result" func_append compiler_flags " $func_quote_arg_result" done IFS=$save_ifs func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Wl,*) func_stripname '-Wl,' '' "$arg" args=$func_stripname_result arg= save_ifs=$IFS; IFS=, for flag in $args; do IFS=$save_ifs func_quote_arg pretty "$flag" func_append arg " $wl$func_quote_arg_result" func_append compiler_flags " $wl$func_quote_arg_result" func_append linker_flags " $func_quote_arg_result" done IFS=$save_ifs func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Xcompiler) prev=xcompiler continue ;; -Xlinker) prev=xlinker continue ;; -XCClinker) prev=xcclinker continue ;; # -msg_* for osf cc -msg_*) func_quote_arg pretty "$arg" arg=$func_quote_arg_result ;; # Flags to be passed through unchanged, with rationale: # -64, -mips[0-9] enable 64-bit mode for the SGI compiler # -r[0-9][0-9]* specify processor for the SGI compiler # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler # +DA*, +DD* enable 64-bit mode for the HP compiler # -q* compiler args for the IBM compiler # -m*, -t[45]*, -txscale* architecture-specific flags for GCC # -F/path path to uninstalled frameworks, gcc on darwin # -p, -pg, --coverage, -fprofile-* profiling flags for GCC # -fstack-protector* stack protector flags for GCC # @file GCC response files # -tp=* Portland pgcc target processor selection # --sysroot=* for sysroot support # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization # -specs=* GCC specs files # -stdlib=* select c++ std lib with clang # -fsanitize=* Clang/GCC memory and address sanitizer # -fuse-ld=* Linker select flags for GCC -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \ -specs=*|-fsanitize=*|-fuse-ld=*) func_quote_arg pretty "$arg" arg=$func_quote_arg_result func_append compile_command " $arg" func_append finalize_command " $arg" func_append compiler_flags " $arg" continue ;; -Z*) if test os2 = "`expr $host : '.*\(os2\)'`"; then # OS/2 uses -Zxxx to specify OS/2-specific options compiler_flags="$compiler_flags $arg" func_append compile_command " $arg" func_append finalize_command " $arg" case $arg in -Zlinker | -Zstack) prev=xcompiler ;; esac continue else # Otherwise treat like 'Some other compiler flag' below func_quote_arg pretty "$arg" arg=$func_quote_arg_result fi ;; # Some other compiler flag. -* | +*) func_quote_arg pretty "$arg" arg=$func_quote_arg_result ;; *.$objext) # A standard object. func_append objs " $arg" ;; *.lo) # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test none = "$pic_object" && test none = "$non_pic_object"; then func_fatal_error "cannot find name of object for '$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result test none = "$pic_object" || { # Prepend the subdirectory the object is found in. pic_object=$xdir$pic_object if test dlfiles = "$prev"; then if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test dlprefiles = "$prev"; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg=$pic_object } # Non-PIC object. if test none != "$non_pic_object"; then # Prepend the subdirectory the object is found in. non_pic_object=$xdir$non_pic_object # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test none = "$pic_object"; then arg=$non_pic_object fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object=$pic_object func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "'$arg' is not a valid libtool object" fi fi ;; *.$libext) # An archive. func_append deplibs " $arg" func_append old_deplibs " $arg" continue ;; *.la) # A libtool-controlled library. func_resolve_sysroot "$arg" if test dlfiles = "$prev"; then # This library was specified with -dlopen. func_append dlfiles " $func_resolve_sysroot_result" prev= elif test dlprefiles = "$prev"; then # The library was specified with -dlpreopen. func_append dlprefiles " $func_resolve_sysroot_result" prev= else func_append deplibs " $func_resolve_sysroot_result" fi continue ;; # Some other compiler argument. *) # Unknown arguments in both finalize_command and compile_command need # to be aesthetically quoted because they are evaled later. func_quote_arg pretty "$arg" arg=$func_quote_arg_result ;; esac # arg # Now actually substitute the argument into the commands. if test -n "$arg"; then func_append compile_command " $arg" func_append finalize_command " $arg" fi done # argument parsing loop test -n "$prev" && \ func_fatal_help "the '$prevarg' option requires an argument" if test yes = "$export_dynamic" && test -n "$export_dynamic_flag_spec"; then eval arg=\"$export_dynamic_flag_spec\" func_append compile_command " $arg" func_append finalize_command " $arg" fi oldlibs= # calculate the name of the file, without its directory func_basename "$output" outputname=$func_basename_result libobjs_save=$libobjs if test -n "$shlibpath_var"; then # get the directories listed in $shlibpath_var eval shlib_search_path=\`\$ECHO \"\$$shlibpath_var\" \| \$SED \'s/:/ /g\'\` else shlib_search_path= fi eval sys_lib_search_path=\"$sys_lib_search_path_spec\" eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" # Definition is injected by LT_CONFIG during libtool generation. func_munge_path_list sys_lib_dlsearch_path "$LT_SYS_LIBRARY_PATH" func_dirname "$output" "/" "" output_objdir=$func_dirname_result$objdir func_to_tool_file "$output_objdir/" tool_output_objdir=$func_to_tool_file_result # Create the object directory. func_mkdir_p "$output_objdir" # Determine the type of output case $output in "") func_fatal_help "you must specify an output file" ;; *.$libext) linkmode=oldlib ;; *.lo | *.$objext) linkmode=obj ;; *.la) linkmode=lib ;; *) linkmode=prog ;; # Anything else should be a program. esac specialdeplibs= libs= # Find all interdependent deplibs by searching for libraries # that are linked more than once (e.g. -la -lb -la) for deplib in $deplibs; do if $opt_preserve_dup_deps; then case "$libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append libs " $deplib" done if test lib = "$linkmode"; then libs="$predeps $libs $compiler_lib_search_path $postdeps" # Compute libraries that are listed more than once in $predeps # $postdeps and mark them as special (i.e., whose duplicates are # not to be eliminated). pre_post_deps= if $opt_duplicate_compiler_generated_deps; then for pre_post_dep in $predeps $postdeps; do case "$pre_post_deps " in *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;; esac func_append pre_post_deps " $pre_post_dep" done fi pre_post_deps= fi deplibs= newdependency_libs= newlib_search_path= need_relink=no # whether we're linking any uninstalled libtool libraries notinst_deplibs= # not-installed libtool libraries notinst_path= # paths that contain not-installed libtool libraries case $linkmode in lib) passes="conv dlpreopen link" for file in $dlfiles $dlprefiles; do case $file in *.la) ;; *) func_fatal_help "libraries can '-dlopen' only libtool libraries: $file" ;; esac done ;; prog) compile_deplibs= finalize_deplibs= alldeplibs=false newdlfiles= newdlprefiles= passes="conv scan dlopen dlpreopen link" ;; *) passes="conv" ;; esac for pass in $passes; do # The preopen pass in lib mode reverses $deplibs; put it back here # so that -L comes before libs that need it for instance... if test lib,link = "$linkmode,$pass"; then ## FIXME: Find the place where the list is rebuilt in the wrong ## order, and fix it there properly tmp_deplibs= for deplib in $deplibs; do tmp_deplibs="$deplib $tmp_deplibs" done deplibs=$tmp_deplibs fi if test lib,link = "$linkmode,$pass" || test prog,scan = "$linkmode,$pass"; then libs=$deplibs deplibs= fi if test prog = "$linkmode"; then case $pass in dlopen) libs=$dlfiles ;; dlpreopen) libs=$dlprefiles ;; link) libs="$deplibs %DEPLIBS% $dependency_libs" ;; esac fi if test lib,dlpreopen = "$linkmode,$pass"; then # Collect and forward deplibs of preopened libtool libs for lib in $dlprefiles; do # Ignore non-libtool-libs dependency_libs= func_resolve_sysroot "$lib" case $lib in *.la) func_source "$func_resolve_sysroot_result" ;; esac # Collect preopened libtool deplibs, except any this library # has declared as weak libs for deplib in $dependency_libs; do func_basename "$deplib" deplib_base=$func_basename_result case " $weak_libs " in *" $deplib_base "*) ;; *) func_append deplibs " $deplib" ;; esac done done libs=$dlprefiles fi if test dlopen = "$pass"; then # Collect dlpreopened libraries save_deplibs=$deplibs deplibs= fi for deplib in $libs; do lib= found=false case $deplib in -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append compiler_flags " $deplib" if test lib = "$linkmode"; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -l*) if test lib != "$linkmode" && test prog != "$linkmode"; then func_warning "'-l' is ignored for archives/objects" continue fi func_stripname '-l' '' "$deplib" name=$func_stripname_result if test lib = "$linkmode"; then searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" else searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" fi for searchdir in $searchdirs; do for search_ext in .la $std_shrext .so .a; do # Search the libtool library lib=$searchdir/lib$name$search_ext if test -f "$lib"; then if test .la = "$search_ext"; then found=: else found=false fi break 2 fi done done if $found; then # deplib is a libtool library # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, # We need to do some special things here, and not later. if test yes = "$allow_libtool_libs_with_static_runtimes"; then case " $predeps $postdeps " in *" $deplib "*) if func_lalib_p "$lib"; then library_names= old_library= func_source "$lib" for l in $old_library $library_names; do ll=$l done if test "X$ll" = "X$old_library"; then # only static version available found=false func_dirname "$lib" "" "." ladir=$func_dirname_result lib=$ladir/$old_library if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" fi continue fi fi ;; *) ;; esac fi else # deplib doesn't seem to be a libtool library if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" fi continue fi ;; # -l *.ltframework) if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" if test lib = "$linkmode"; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -L*) case $linkmode in lib) deplibs="$deplib $deplibs" test conv = "$pass" && continue newdependency_libs="$deplib $newdependency_libs" func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; prog) if test conv = "$pass"; then deplibs="$deplib $deplibs" continue fi if test scan = "$pass"; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; *) func_warning "'-L' is ignored for archives/objects" ;; esac # linkmode continue ;; # -L -R*) if test link = "$pass"; then func_stripname '-R' '' "$deplib" func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # Make sure the xrpath contains only unique directories. case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac fi deplibs="$deplib $deplibs" continue ;; *.la) func_resolve_sysroot "$deplib" lib=$func_resolve_sysroot_result ;; *.$libext) if test conv = "$pass"; then deplibs="$deplib $deplibs" continue fi case $linkmode in lib) # Linking convenience modules into shared libraries is allowed, # but linking other static libraries is non-portable. case " $dlpreconveniencelibs " in *" $deplib "*) ;; *) valid_a_lib=false case $deplibs_check_method in match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \ | $EGREP "$match_pattern_regex" > /dev/null; then valid_a_lib=: fi ;; pass_all) valid_a_lib=: ;; esac if $valid_a_lib; then echo $ECHO "*** Warning: Linking the shared library $output against the" $ECHO "*** static library $deplib is not portable!" deplibs="$deplib $deplibs" else echo $ECHO "*** Warning: Trying to link with static lib archive $deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because the file extensions .$libext of this argument makes me believe" echo "*** that it is just a static archive that I should not use here." fi ;; esac continue ;; prog) if test link != "$pass"; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi continue ;; esac # linkmode ;; # *.$libext *.lo | *.$objext) if test conv = "$pass"; then deplibs="$deplib $deplibs" elif test prog = "$linkmode"; then if test dlpreopen = "$pass" || test yes != "$dlopen_support" || test no = "$build_libtool_libs"; then # If there is no dlopen support or we're linking statically, # we need to preload. func_append newdlprefiles " $deplib" compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append newdlfiles " $deplib" fi fi continue ;; %DEPLIBS%) alldeplibs=: continue ;; esac # case $deplib $found || test -f "$lib" \ || func_fatal_error "cannot find the library '$lib' or unhandled argument '$deplib'" # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$lib" \ || func_fatal_error "'$lib' is not a valid libtool archive" func_dirname "$lib" "" "." ladir=$func_dirname_result dlname= dlopen= dlpreopen= libdir= library_names= old_library= inherited_linker_flags= # If the library was installed with an old release of libtool, # it will not redefine variables installed, or shouldnotlink installed=yes shouldnotlink=no avoidtemprpath= # Read the .la file func_source "$lib" # Convert "-framework foo" to "foo.ltframework" if test -n "$inherited_linker_flags"; then tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'` for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do case " $new_inherited_linker_flags " in *" $tmp_inherited_linker_flag "*) ;; *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";; esac done fi dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` if test lib,link = "$linkmode,$pass" || test prog,scan = "$linkmode,$pass" || { test prog != "$linkmode" && test lib != "$linkmode"; }; then test -n "$dlopen" && func_append dlfiles " $dlopen" test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen" fi if test conv = "$pass"; then # Only check for convenience libraries deplibs="$lib $deplibs" if test -z "$libdir"; then if test -z "$old_library"; then func_fatal_error "cannot find name of link library for '$lib'" fi # It is a libtool convenience library, so add in its objects. func_append convenience " $ladir/$objdir/$old_library" func_append old_convenience " $ladir/$objdir/$old_library" elif test prog != "$linkmode" && test lib != "$linkmode"; then func_fatal_error "'$lib' is not a convenience library" fi tmp_libs= for deplib in $dependency_libs; do deplibs="$deplib $deplibs" if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done continue fi # $pass = conv # Get the name of the library we link against. linklib= if test -n "$old_library" && { test yes = "$prefer_static_libs" || test built,no = "$prefer_static_libs,$installed"; }; then linklib=$old_library else for l in $old_library $library_names; do linklib=$l done fi if test -z "$linklib"; then func_fatal_error "cannot find name of link library for '$lib'" fi # This library was specified with -dlopen. if test dlopen = "$pass"; then test -z "$libdir" \ && func_fatal_error "cannot -dlopen a convenience library: '$lib'" if test -z "$dlname" || test yes != "$dlopen_support" || test no = "$build_libtool_libs" then # If there is no dlname, no dlopen support or we're linking # statically, we need to preload. We also need to preload any # dependent libraries so libltdl's deplib preloader doesn't # bomb out in the load deplibs phase. func_append dlprefiles " $lib $dependency_libs" else func_append newdlfiles " $lib" fi continue fi # $pass = dlopen # We need an absolute path. case $ladir in [\\/]* | [A-Za-z]:[\\/]*) abs_ladir=$ladir ;; *) abs_ladir=`cd "$ladir" && pwd` if test -z "$abs_ladir"; then func_warning "cannot determine absolute directory name of '$ladir'" func_warning "passing it literally to the linker, although it might fail" abs_ladir=$ladir fi ;; esac func_basename "$lib" laname=$func_basename_result # Find the relevant object directory and library name. if test yes = "$installed"; then if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then func_warning "library '$lib' was moved." dir=$ladir absdir=$abs_ladir libdir=$abs_ladir else dir=$lt_sysroot$libdir absdir=$lt_sysroot$libdir fi test yes = "$hardcode_automatic" && avoidtemprpath=yes else if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then dir=$ladir absdir=$abs_ladir # Remove this search path later func_append notinst_path " $abs_ladir" else dir=$ladir/$objdir absdir=$abs_ladir/$objdir # Remove this search path later func_append notinst_path " $abs_ladir" fi fi # $installed = yes func_stripname 'lib' '.la' "$laname" name=$func_stripname_result # This library was specified with -dlpreopen. if test dlpreopen = "$pass"; then if test -z "$libdir" && test prog = "$linkmode"; then func_fatal_error "only libraries may -dlpreopen a convenience library: '$lib'" fi case $host in # special handling for platforms with PE-DLLs. *cygwin* | *mingw* | *cegcc* ) # Linker will automatically link against shared library if both # static and shared are present. Therefore, ensure we extract # symbols from the import library if a shared library is present # (otherwise, the dlopen module name will be incorrect). We do # this by putting the import library name into $newdlprefiles. # We recover the dlopen module name by 'saving' the la file # name in a special purpose variable, and (later) extracting the # dlname from the la file. if test -n "$dlname"; then func_tr_sh "$dir/$linklib" eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname" func_append newdlprefiles " $dir/$linklib" else func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" fi ;; * ) # Prefer using a static library (so that no silly _DYNAMIC symbols # are required to link). if test -n "$old_library"; then func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" # Otherwise, use the dlname, so that lt_dlopen finds it. elif test -n "$dlname"; then func_append newdlprefiles " $dir/$dlname" else func_append newdlprefiles " $dir/$linklib" fi ;; esac fi # $pass = dlpreopen if test -z "$libdir"; then # Link the convenience library if test lib = "$linkmode"; then deplibs="$dir/$old_library $deplibs" elif test prog,link = "$linkmode,$pass"; then compile_deplibs="$dir/$old_library $compile_deplibs" finalize_deplibs="$dir/$old_library $finalize_deplibs" else deplibs="$lib $deplibs" # used for prog,scan pass fi continue fi if test prog = "$linkmode" && test link != "$pass"; then func_append newlib_search_path " $ladir" deplibs="$lib $deplibs" linkalldeplibs=false if test no != "$link_all_deplibs" || test -z "$library_names" || test no = "$build_libtool_libs"; then linkalldeplibs=: fi tmp_libs= for deplib in $dependency_libs; do case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; esac # Need to link against all dependency_libs? if $linkalldeplibs; then deplibs="$deplib $deplibs" else # Need to hardcode shared library paths # or/and link against static libraries newdependency_libs="$deplib $newdependency_libs" fi if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done # for deplib continue fi # $linkmode = prog... if test prog,link = "$linkmode,$pass"; then if test -n "$library_names" && { { test no = "$prefer_static_libs" || test built,yes = "$prefer_static_libs,$installed"; } || test -z "$old_library"; }; then # We need to hardcode the library path if test -n "$shlibpath_var" && test -z "$avoidtemprpath"; then # Make sure the rpath contains only unique directories. case $temp_rpath: in *"$absdir:"*) ;; *) func_append temp_rpath "$absdir:" ;; esac fi # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi # $linkmode,$pass = prog,link... if $alldeplibs && { test pass_all = "$deplibs_check_method" || { test yes = "$build_libtool_libs" && test -n "$library_names"; }; }; then # We only need to search for static libraries continue fi fi link_static=no # Whether the deplib will be linked statically use_static_libs=$prefer_static_libs if test built = "$use_static_libs" && test yes = "$installed"; then use_static_libs=no fi if test -n "$library_names" && { test no = "$use_static_libs" || test -z "$old_library"; }; then case $host in *cygwin* | *mingw* | *cegcc* | *os2*) # No point in relinking DLLs because paths are not encoded func_append notinst_deplibs " $lib" need_relink=no ;; *) if test no = "$installed"; then func_append notinst_deplibs " $lib" need_relink=yes fi ;; esac # This is a shared library # Warn about portability, can't link against -module's on some # systems (darwin). Don't bleat about dlopened modules though! dlopenmodule= for dlpremoduletest in $dlprefiles; do if test "X$dlpremoduletest" = "X$lib"; then dlopenmodule=$dlpremoduletest break fi done if test -z "$dlopenmodule" && test yes = "$shouldnotlink" && test link = "$pass"; then echo if test prog = "$linkmode"; then $ECHO "*** Warning: Linking the executable $output against the loadable module" else $ECHO "*** Warning: Linking the shared library $output against the loadable module" fi $ECHO "*** $linklib is not portable!" fi if test lib = "$linkmode" && test yes = "$hardcode_into_libs"; then # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi if test -n "$old_archive_from_expsyms_cmds"; then # figure out the soname set dummy $library_names shift realname=$1 shift libname=`eval "\\$ECHO \"$libname_spec\""` # use dlname if we got it. it's perfectly good, no? if test -n "$dlname"; then soname=$dlname elif test -n "$soname_spec"; then # bleh windows case $host in *cygwin* | mingw* | *cegcc* | *os2*) func_arith $current - $age major=$func_arith_result versuffix=-$major ;; esac eval soname=\"$soname_spec\" else soname=$realname fi # Make a new name for the extract_expsyms_cmds to use soroot=$soname func_basename "$soroot" soname=$func_basename_result func_stripname 'lib' '.dll' "$soname" newlib=libimp-$func_stripname_result.a # If the library has no export list, then create one now if test -f "$output_objdir/$soname-def"; then : else func_verbose "extracting exported symbol list from '$soname'" func_execute_cmds "$extract_expsyms_cmds" 'exit $?' fi # Create $newlib if test -f "$output_objdir/$newlib"; then :; else func_verbose "generating import library for '$soname'" func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' fi # make sure the library variables are pointing to the new library dir=$output_objdir linklib=$newlib fi # test -n "$old_archive_from_expsyms_cmds" if test prog = "$linkmode" || test relink != "$opt_mode"; then add_shlibpath= add_dir= add= lib_linked=yes case $hardcode_action in immediate | unsupported) if test no = "$hardcode_direct"; then add=$dir/$linklib case $host in *-*-sco3.2v5.0.[024]*) add_dir=-L$dir ;; *-*-sysv4*uw2*) add_dir=-L$dir ;; *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ *-*-unixware7*) add_dir=-L$dir ;; *-*-darwin* ) # if the lib is a (non-dlopened) module then we cannot # link against it, someone is ignoring the earlier warnings if /usr/bin/file -L $add 2> /dev/null | $GREP ": [^:]* bundle" >/dev/null; then if test "X$dlopenmodule" != "X$lib"; then $ECHO "*** Warning: lib $linklib is a module, not a shared library" if test -z "$old_library"; then echo echo "*** And there doesn't seem to be a static archive available" echo "*** The link will probably fail, sorry" else add=$dir/$old_library fi elif test -n "$old_library"; then add=$dir/$old_library fi fi esac elif test no = "$hardcode_minus_L"; then case $host in *-*-sunos*) add_shlibpath=$dir ;; esac add_dir=-L$dir add=-l$name elif test no = "$hardcode_shlibpath_var"; then add_shlibpath=$dir add=-l$name else lib_linked=no fi ;; relink) if test yes = "$hardcode_direct" && test no = "$hardcode_direct_absolute"; then add=$dir/$linklib elif test yes = "$hardcode_minus_L"; then add_dir=-L$absdir # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add=-l$name elif test yes = "$hardcode_shlibpath_var"; then add_shlibpath=$dir add=-l$name else lib_linked=no fi ;; *) lib_linked=no ;; esac if test yes != "$lib_linked"; then func_fatal_configuration "unsupported hardcode properties" fi if test -n "$add_shlibpath"; then case :$compile_shlibpath: in *":$add_shlibpath:"*) ;; *) func_append compile_shlibpath "$add_shlibpath:" ;; esac fi if test prog = "$linkmode"; then test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" test -n "$add" && compile_deplibs="$add $compile_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" if test yes != "$hardcode_direct" && test yes != "$hardcode_minus_L" && test yes = "$hardcode_shlibpath_var"; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac fi fi fi if test prog = "$linkmode" || test relink = "$opt_mode"; then add_shlibpath= add_dir= add= # Finalize command for both is simple: just hardcode it. if test yes = "$hardcode_direct" && test no = "$hardcode_direct_absolute"; then add=$libdir/$linklib elif test yes = "$hardcode_minus_L"; then add_dir=-L$libdir add=-l$name elif test yes = "$hardcode_shlibpath_var"; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac add=-l$name elif test yes = "$hardcode_automatic"; then if test -n "$inst_prefix_dir" && test -f "$inst_prefix_dir$libdir/$linklib"; then add=$inst_prefix_dir$libdir/$linklib else add=$libdir/$linklib fi else # We cannot seem to hardcode it, guess we'll fake it. add_dir=-L$libdir # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add=-l$name fi if test prog = "$linkmode"; then test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" test -n "$add" && finalize_deplibs="$add $finalize_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" fi fi elif test prog = "$linkmode"; then # Here we assume that one of hardcode_direct or hardcode_minus_L # is not unsupported. This is valid on all known static and # shared platforms. if test unsupported != "$hardcode_direct"; then test -n "$old_library" && linklib=$old_library compile_deplibs="$dir/$linklib $compile_deplibs" finalize_deplibs="$dir/$linklib $finalize_deplibs" else compile_deplibs="-l$name -L$dir $compile_deplibs" finalize_deplibs="-l$name -L$dir $finalize_deplibs" fi elif test yes = "$build_libtool_libs"; then # Not a shared library if test pass_all != "$deplibs_check_method"; then # We're trying link a shared library against a static one # but the system doesn't support it. # Just print a warning and add the library to dependency_libs so # that the program can be linked against the static library. echo $ECHO "*** Warning: This system cannot link to static lib archive $lib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have." if test yes = "$module"; then echo "*** But as you try to build a module library, libtool will still create " echo "*** a static module, that should work as long as the dlopening application" echo "*** is linked with the -dlopen flag to resolve symbols at runtime." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using 'nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** 'nm' from GNU binutils and a full rebuild may help." fi if test no = "$build_old_libs"; then build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi else deplibs="$dir/$old_library $deplibs" link_static=yes fi fi # link shared/static library? if test lib = "$linkmode"; then if test -n "$dependency_libs" && { test yes != "$hardcode_into_libs" || test yes = "$build_old_libs" || test yes = "$link_static"; }; then # Extract -R from dependency_libs temp_deplibs= for libdir in $dependency_libs; do case $libdir in -R*) func_stripname '-R' '' "$libdir" temp_xrpath=$func_stripname_result case " $xrpath " in *" $temp_xrpath "*) ;; *) func_append xrpath " $temp_xrpath";; esac;; *) func_append temp_deplibs " $libdir";; esac done dependency_libs=$temp_deplibs fi func_append newlib_search_path " $absdir" # Link against this library test no = "$link_static" && newdependency_libs="$abs_ladir/$laname $newdependency_libs" # ... and its dependency_libs tmp_libs= for deplib in $dependency_libs; do newdependency_libs="$deplib $newdependency_libs" case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result";; *) func_resolve_sysroot "$deplib" ;; esac if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $func_resolve_sysroot_result "*) func_append specialdeplibs " $func_resolve_sysroot_result" ;; esac fi func_append tmp_libs " $func_resolve_sysroot_result" done if test no != "$link_all_deplibs"; then # Add the search paths of all dependency libraries for deplib in $dependency_libs; do path= case $deplib in -L*) path=$deplib ;; *.la) func_resolve_sysroot "$deplib" deplib=$func_resolve_sysroot_result func_dirname "$deplib" "" "." dir=$func_dirname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) absdir=$dir ;; *) absdir=`cd "$dir" && pwd` if test -z "$absdir"; then func_warning "cannot determine absolute directory name of '$dir'" absdir=$dir fi ;; esac if $GREP "^installed=no" $deplib > /dev/null; then case $host in *-*-darwin*) depdepl= eval deplibrary_names=`$SED -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` if test -n "$deplibrary_names"; then for tmp in $deplibrary_names; do depdepl=$tmp done if test -f "$absdir/$objdir/$depdepl"; then depdepl=$absdir/$objdir/$depdepl darwin_install_name=`$OTOOL -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` if test -z "$darwin_install_name"; then darwin_install_name=`$OTOOL64 -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` fi func_append compiler_flags " $wl-dylib_file $wl$darwin_install_name:$depdepl" func_append linker_flags " -dylib_file $darwin_install_name:$depdepl" path= fi fi ;; *) path=-L$absdir/$objdir ;; esac else eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` test -z "$libdir" && \ func_fatal_error "'$deplib' is not a valid libtool archive" test "$absdir" != "$libdir" && \ func_warning "'$deplib' seems to be moved" path=-L$absdir fi ;; esac case " $deplibs " in *" $path "*) ;; *) deplibs="$path $deplibs" ;; esac done fi # link_all_deplibs != no fi # linkmode = lib done # for deplib in $libs if test link = "$pass"; then if test prog = "$linkmode"; then compile_deplibs="$new_inherited_linker_flags $compile_deplibs" finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" else compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` fi fi dependency_libs=$newdependency_libs if test dlpreopen = "$pass"; then # Link the dlpreopened libraries before other libraries for deplib in $save_deplibs; do deplibs="$deplib $deplibs" done fi if test dlopen != "$pass"; then test conv = "$pass" || { # Make sure lib_search_path contains only unique directories. lib_search_path= for dir in $newlib_search_path; do case "$lib_search_path " in *" $dir "*) ;; *) func_append lib_search_path " $dir" ;; esac done newlib_search_path= } if test prog,link = "$linkmode,$pass"; then vars="compile_deplibs finalize_deplibs" else vars=deplibs fi for var in $vars dependency_libs; do # Add libraries to $var in reverse order eval tmp_libs=\"\$$var\" new_libs= for deplib in $tmp_libs; do # FIXME: Pedantically, this is the right thing to do, so # that some nasty dependency loop isn't accidentally # broken: #new_libs="$deplib $new_libs" # Pragmatically, this seems to cause very few problems in # practice: case $deplib in -L*) new_libs="$deplib $new_libs" ;; -R*) ;; *) # And here is the reason: when a library appears more # than once as an explicit dependence of a library, or # is implicitly linked in more than once by the # compiler, it is considered special, and multiple # occurrences thereof are not removed. Compare this # with having the same library being listed as a # dependency of multiple other libraries: in this case, # we know (pedantically, we assume) the library does not # need to be listed more than once, so we keep only the # last copy. This is not always right, but it is rare # enough that we require users that really mean to play # such unportable linking tricks to link the library # using -Wl,-lname, so that libtool does not consider it # for duplicate removal. case " $specialdeplibs " in *" $deplib "*) new_libs="$deplib $new_libs" ;; *) case " $new_libs " in *" $deplib "*) ;; *) new_libs="$deplib $new_libs" ;; esac ;; esac ;; esac done tmp_libs= for deplib in $new_libs; do case $deplib in -L*) case " $tmp_libs " in *" $deplib "*) ;; *) func_append tmp_libs " $deplib" ;; esac ;; *) func_append tmp_libs " $deplib" ;; esac done eval $var=\"$tmp_libs\" done # for var fi # Add Sun CC postdeps if required: test CXX = "$tagname" && { case $host_os in linux*) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 func_suncc_cstd_abi if test no != "$suncc_use_cstd_abi"; then func_append postdeps ' -library=Cstd -library=Crun' fi ;; esac ;; solaris*) func_cc_basename "$CC" case $func_cc_basename_result in CC* | sunCC*) func_suncc_cstd_abi if test no != "$suncc_use_cstd_abi"; then func_append postdeps ' -library=Cstd -library=Crun' fi ;; esac ;; esac } # Last step: remove runtime libs from dependency_libs # (they stay in deplibs) tmp_libs= for i in $dependency_libs; do case " $predeps $postdeps $compiler_lib_search_path " in *" $i "*) i= ;; esac if test -n "$i"; then func_append tmp_libs " $i" fi done dependency_libs=$tmp_libs done # for pass if test prog = "$linkmode"; then dlfiles=$newdlfiles fi if test prog = "$linkmode" || test lib = "$linkmode"; then dlprefiles=$newdlprefiles fi case $linkmode in oldlib) if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then func_warning "'-dlopen' is ignored for archives" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "'-l' and '-L' are ignored for archives" ;; esac test -n "$rpath" && \ func_warning "'-rpath' is ignored for archives" test -n "$xrpath" && \ func_warning "'-R' is ignored for archives" test -n "$vinfo" && \ func_warning "'-version-info/-version-number' is ignored for archives" test -n "$release" && \ func_warning "'-release' is ignored for archives" test -n "$export_symbols$export_symbols_regex" && \ func_warning "'-export-symbols' is ignored for archives" # Now set the variables for building old libraries. build_libtool_libs=no oldlibs=$output func_append objs "$old_deplibs" ;; lib) # Make sure we only generate libraries of the form 'libNAME.la'. case $outputname in lib*) func_stripname 'lib' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" ;; *) test no = "$module" \ && func_fatal_help "libtool library '$output' must begin with 'lib'" if test no != "$need_lib_prefix"; then # Add the "lib" prefix for modules if required func_stripname '' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" else func_stripname '' '.la' "$outputname" libname=$func_stripname_result fi ;; esac if test -n "$objs"; then if test pass_all != "$deplibs_check_method"; then func_fatal_error "cannot build libtool library '$output' from non-libtool objects on this host:$objs" else echo $ECHO "*** Warning: Linking the shared library $output against the non-libtool" $ECHO "*** objects $objs is not portable!" func_append libobjs " $objs" fi fi test no = "$dlself" \ || func_warning "'-dlopen self' is ignored for libtool libraries" set dummy $rpath shift test 1 -lt "$#" \ && func_warning "ignoring multiple '-rpath's for a libtool library" install_libdir=$1 oldlibs= if test -z "$rpath"; then if test yes = "$build_libtool_libs"; then # Building a libtool convenience library. # Some compilers have problems with a '.al' extension so # convenience libraries should have the same extension an # archive normally would. oldlibs="$output_objdir/$libname.$libext $oldlibs" build_libtool_libs=convenience build_old_libs=yes fi test -n "$vinfo" && \ func_warning "'-version-info/-version-number' is ignored for convenience libraries" test -n "$release" && \ func_warning "'-release' is ignored for convenience libraries" else # Parse the version information argument. save_ifs=$IFS; IFS=: set dummy $vinfo 0 0 0 shift IFS=$save_ifs test -n "$7" && \ func_fatal_help "too many parameters to '-version-info'" # convert absolute version numbers to libtool ages # this retains compatibility with .la files and attempts # to make the code below a bit more comprehensible case $vinfo_number in yes) number_major=$1 number_minor=$2 number_revision=$3 # # There are really only two kinds -- those that # use the current revision as the major version # and those that subtract age and use age as # a minor version. But, then there is irix # that has an extra 1 added just for fun # case $version_type in # correct linux to gnu/linux during the next big refactor darwin|freebsd-elf|linux|osf|windows|none) func_arith $number_major + $number_minor current=$func_arith_result age=$number_minor revision=$number_revision ;; freebsd-aout|qnx|sunos) current=$number_major revision=$number_minor age=0 ;; irix|nonstopux) func_arith $number_major + $number_minor current=$func_arith_result age=$number_minor revision=$number_minor lt_irix_increment=no ;; esac ;; no) current=$1 revision=$2 age=$3 ;; esac # Check that each of the things are valid numbers. case $current in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "CURRENT '$current' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac case $revision in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "REVISION '$revision' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac case $age in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "AGE '$age' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac if test "$age" -gt "$current"; then func_error "AGE '$age' is greater than the current interface number '$current'" func_fatal_error "'$vinfo' is not valid version information" fi # Calculate the version variables. major= versuffix= verstring= case $version_type in none) ;; darwin) # Like Linux, but with the current version available in # verstring for coding it into the library header func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision # Darwin ld doesn't like 0 for these options... func_arith $current + 1 minor_current=$func_arith_result xlcverstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" # On Darwin other compilers case $CC in nagfor*) verstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" ;; *) verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" ;; esac ;; freebsd-aout) major=.$current versuffix=.$current.$revision ;; freebsd-elf) func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision ;; irix | nonstopux) if test no = "$lt_irix_increment"; then func_arith $current - $age else func_arith $current - $age + 1 fi major=$func_arith_result case $version_type in nonstopux) verstring_prefix=nonstopux ;; *) verstring_prefix=sgi ;; esac verstring=$verstring_prefix$major.$revision # Add in all the interfaces that we are compatible with. loop=$revision while test 0 -ne "$loop"; do func_arith $revision - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring=$verstring_prefix$major.$iface:$verstring done # Before this point, $major must not contain '.'. major=.$major versuffix=$major.$revision ;; linux) # correct to gnu/linux during the next big refactor func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision ;; osf) func_arith $current - $age major=.$func_arith_result versuffix=.$current.$age.$revision verstring=$current.$age.$revision # Add in all the interfaces that we are compatible with. loop=$age while test 0 -ne "$loop"; do func_arith $current - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring=$verstring:$iface.0 done # Make executables depend on our current version. func_append verstring ":$current.0" ;; qnx) major=.$current versuffix=.$current ;; sco) major=.$current versuffix=.$current ;; sunos) major=.$current versuffix=.$current.$revision ;; windows) # Use '-' rather than '.', since we only want one # extension on DOS 8.3 file systems. func_arith $current - $age major=$func_arith_result versuffix=-$major ;; *) func_fatal_configuration "unknown library version type '$version_type'" ;; esac # Clear the version info if we defaulted, and they specified a release. if test -z "$vinfo" && test -n "$release"; then major= case $version_type in darwin) # we can't check for "0.0" in archive_cmds due to quoting # problems, so we reset it completely verstring= ;; *) verstring=0.0 ;; esac if test no = "$need_version"; then versuffix= else versuffix=.0.0 fi fi # Remove version info from name if versioning should be avoided if test yes,no = "$avoid_version,$need_version"; then major= versuffix= verstring= fi # Check to see if the archive will have undefined symbols. if test yes = "$allow_undefined"; then if test unsupported = "$allow_undefined_flag"; then if test yes = "$build_old_libs"; then func_warning "undefined symbols not allowed in $host shared libraries; building static only" build_libtool_libs=no else func_fatal_error "can't build $host shared library unless -no-undefined is specified" fi fi else # Don't allow undefined symbols. allow_undefined_flag=$no_undefined_flag fi fi func_generate_dlsyms "$libname" "$libname" : func_append libobjs " $symfileobj" test " " = "$libobjs" && libobjs= if test relink != "$opt_mode"; then # Remove our outputs, but don't remove object files since they # may have been created when compiling PIC objects. removelist= tempremovelist=`$ECHO "$output_objdir/*"` for p in $tempremovelist; do case $p in *.$objext | *.gcno) ;; $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/$libname$release.*) if test -n "$precious_files_regex"; then if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 then continue fi fi func_append removelist " $p" ;; *) ;; esac done test -n "$removelist" && \ func_show_eval "${RM}r \$removelist" fi # Now set the variables for building old libraries. if test yes = "$build_old_libs" && test convenience != "$build_libtool_libs"; then func_append oldlibs " $output_objdir/$libname.$libext" # Transform .lo files to .o files. oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; $lo2o" | $NL2SP` fi # Eliminate all temporary directories. #for path in $notinst_path; do # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"` # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"` # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"` #done if test -n "$xrpath"; then # If the user specified any rpath flags, then add them. temp_xrpath= for libdir in $xrpath; do func_replace_sysroot "$libdir" func_append temp_xrpath " -R$func_replace_sysroot_result" case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done if test yes != "$hardcode_into_libs" || test yes = "$build_old_libs"; then dependency_libs="$temp_xrpath $dependency_libs" fi fi # Make sure dlfiles contains only unique files that won't be dlpreopened old_dlfiles=$dlfiles dlfiles= for lib in $old_dlfiles; do case " $dlprefiles $dlfiles " in *" $lib "*) ;; *) func_append dlfiles " $lib" ;; esac done # Make sure dlprefiles contains only unique files old_dlprefiles=$dlprefiles dlprefiles= for lib in $old_dlprefiles; do case "$dlprefiles " in *" $lib "*) ;; *) func_append dlprefiles " $lib" ;; esac done if test yes = "$build_libtool_libs"; then if test -n "$rpath"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) # these systems don't actually have a c library (as such)! ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C library is in the System framework func_append deplibs " System.ltframework" ;; *-*-netbsd*) # Don't link with libc until the a.out ld.so is fixed. ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc due to us having libc/libc_r. ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work ;; *) # Add libc to deplibs on all other systems if necessary. if test yes = "$build_libtool_need_lc"; then func_append deplibs " -lc" fi ;; esac fi # Transform deplibs into only deplibs that can be linked in shared. name_save=$name libname_save=$libname release_save=$release versuffix_save=$versuffix major_save=$major # I'm not sure if I'm treating the release correctly. I think # release should show up in the -l (ie -lgmp5) so we don't want to # add it in twice. Is that correct? release= versuffix= major= newdeplibs= droppeddeps=no case $deplibs_check_method in pass_all) # Don't check for shared/static. Everything works. # This might be a little naive. We might want to check # whether the library exists or not. But this is on # osf3 & osf4 and I'm not really sure... Just # implementing what was already the behavior. newdeplibs=$deplibs ;; test_compile) # This code stresses the "libraries are programs" paradigm to its # limits. Maybe even breaks it. We compile a program, linking it # against the deplibs as a proxy for the library. Then we can check # whether they linked in statically or dynamically with ldd. $opt_dry_run || $RM conftest.c cat > conftest.c </dev/null` $nocaseglob else potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` fi for potent_lib in $potential_libs; do # Follow soft links. if ls -lLd "$potent_lib" 2>/dev/null | $GREP " -> " >/dev/null; then continue fi # The statement above tries to avoid entering an # endless loop below, in case of cyclic links. # We might still enter an endless loop, since a link # loop can be closed while we follow links, # but so what? potlib=$potent_lib while test -h "$potlib" 2>/dev/null; do potliblink=`ls -ld $potlib | $SED 's/.* -> //'` case $potliblink in [\\/]* | [A-Za-z]:[\\/]*) potlib=$potliblink;; *) potlib=`$ECHO "$potlib" | $SED 's|[^/]*$||'`"$potliblink";; esac done if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | $SED -e 10q | $EGREP "$file_magic_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib= break 2 fi done done fi if test -n "$a_deplib"; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib"; then $ECHO "*** with $libname but no candidates were found. (...for file magic test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a file magic. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` for a_deplib in $deplibs; do case $a_deplib in -l*) func_stripname -l '' "$a_deplib" name=$func_stripname_result if test yes = "$allow_libtool_libs_with_static_runtimes"; then case " $predeps $postdeps " in *" $a_deplib "*) func_append newdeplibs " $a_deplib" a_deplib= ;; esac fi if test -n "$a_deplib"; then libname=`eval "\\$ECHO \"$libname_spec\""` for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do potential_libs=`ls $i/$libname[.-]* 2>/dev/null` for potent_lib in $potential_libs; do potlib=$potent_lib # see symlink-check above in file_magic test if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \ $EGREP "$match_pattern_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib= break 2 fi done done fi if test -n "$a_deplib"; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib"; then $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a regex pattern. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; none | unknown | *) newdeplibs= tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'` if test yes = "$allow_libtool_libs_with_static_runtimes"; then for i in $predeps $postdeps; do # can't use Xsed below, because $i might contain '/' tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s|$i||"` done fi case $tmp_deplibs in *[!\ \ ]*) echo if test none = "$deplibs_check_method"; then echo "*** Warning: inter-library dependencies are not supported in this platform." else echo "*** Warning: inter-library dependencies are not known to be supported." fi echo "*** All declared inter-library dependencies are being dropped." droppeddeps=yes ;; esac ;; esac versuffix=$versuffix_save major=$major_save release=$release_save libname=$libname_save name=$name_save case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library with the System framework newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac if test yes = "$droppeddeps"; then if test yes = "$module"; then echo echo "*** Warning: libtool could not satisfy all declared inter-library" $ECHO "*** dependencies of module $libname. Therefore, libtool will create" echo "*** a static module, that should work as long as the dlopening" echo "*** application is linked with the -dlopen flag." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using 'nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** 'nm' from GNU binutils and a full rebuild may help." fi if test no = "$build_old_libs"; then oldlibs=$output_objdir/$libname.$libext build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi else echo "*** The inter-library dependencies that have been dropped here will be" echo "*** automatically added whenever a program is linked with this library" echo "*** or is declared to -dlopen it." if test no = "$allow_undefined"; then echo echo "*** Since this library must not contain undefined symbols," echo "*** because either the platform does not support them or" echo "*** it was explicitly requested with -no-undefined," echo "*** libtool will only create a static version of it." if test no = "$build_old_libs"; then oldlibs=$output_objdir/$libname.$libext build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi fi fi # Done checking deplibs! deplibs=$newdeplibs fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" case $host in *-*-darwin*) newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done deplibs=$new_libs # All the library-specific variables (install_libdir is set above). library_names= old_library= dlname= # Test again, we may have decided not to build it any more if test yes = "$build_libtool_libs"; then # Remove $wl instances when linking with ld. # FIXME: should test the right _cmds variable. case $archive_cmds in *\$LD\ *) wl= ;; esac if test yes = "$hardcode_into_libs"; then # Hardcode the library paths hardcode_libdirs= dep_rpath= rpath=$finalize_rpath test relink = "$opt_mode" || rpath=$compile_rpath$rpath for libdir in $rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then func_replace_sysroot "$libdir" libdir=$func_replace_sysroot_result if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append dep_rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval "dep_rpath=\"$hardcode_libdir_flag_spec\"" fi if test -n "$runpath_var" && test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" fi test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" fi shlibpath=$finalize_shlibpath test relink = "$opt_mode" || shlibpath=$compile_shlibpath$shlibpath if test -n "$shlibpath"; then eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" fi # Get the real and link names of the library. eval shared_ext=\"$shrext_cmds\" eval library_names=\"$library_names_spec\" set dummy $library_names shift realname=$1 shift if test -n "$soname_spec"; then eval soname=\"$soname_spec\" else soname=$realname fi if test -z "$dlname"; then dlname=$soname fi lib=$output_objdir/$realname linknames= for link do func_append linknames " $link" done # Use standard objects if they are pic test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP` test "X$libobjs" = "X " && libobjs= delfiles= if test -n "$export_symbols" && test -n "$include_expsyms"; then $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" export_symbols=$output_objdir/$libname.uexp func_append delfiles " $export_symbols" fi orig_export_symbols= case $host_os in cygwin* | mingw* | cegcc*) if test -n "$export_symbols" && test -z "$export_symbols_regex"; then # exporting using user supplied symfile func_dll_def_p "$export_symbols" || { # and it's NOT already a .def file. Must figure out # which of the given symbols are data symbols and tag # them as such. So, trigger use of export_symbols_cmds. # export_symbols gets reassigned inside the "prepare # the list of exported symbols" if statement, so the # include_expsyms logic still works. orig_export_symbols=$export_symbols export_symbols= always_export_symbols=yes } fi ;; esac # Prepare the list of exported symbols if test -z "$export_symbols"; then if test yes = "$always_export_symbols" || test -n "$export_symbols_regex"; then func_verbose "generating symbol list for '$libname.la'" export_symbols=$output_objdir/$libname.exp $opt_dry_run || $RM $export_symbols cmds=$export_symbols_cmds save_ifs=$IFS; IFS='~' for cmd1 in $cmds; do IFS=$save_ifs # Take the normal branch if the nm_file_list_spec branch # doesn't work or if tool conversion is not needed. case $nm_file_list_spec~$to_tool_file_cmd in *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*) try_normal_branch=yes eval cmd=\"$cmd1\" func_len " $cmd" len=$func_len_result ;; *) try_normal_branch=no ;; esac if test yes = "$try_normal_branch" \ && { test "$len" -lt "$max_cmd_len" \ || test "$max_cmd_len" -le -1; } then func_show_eval "$cmd" 'exit $?' skipped_export=false elif test -n "$nm_file_list_spec"; then func_basename "$output" output_la=$func_basename_result save_libobjs=$libobjs save_output=$output output=$output_objdir/$output_la.nm func_to_tool_file "$output" libobjs=$nm_file_list_spec$func_to_tool_file_result func_append delfiles " $output" func_verbose "creating $NM input file list: $output" for obj in $save_libobjs; do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > "$output" eval cmd=\"$cmd1\" func_show_eval "$cmd" 'exit $?' output=$save_output libobjs=$save_libobjs skipped_export=false else # The command line is too long to execute in one step. func_verbose "using reloadable object file for export list..." skipped_export=: # Break out early, otherwise skipped_export may be # set to false by a later but shorter cmd. break fi done IFS=$save_ifs if test -n "$export_symbols_regex" && test : != "$skipped_export"; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi fi if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols=$export_symbols test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test : != "$skipped_export" && test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for '$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands, which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi tmp_deplibs= for test_deplib in $deplibs; do case " $convenience " in *" $test_deplib "*) ;; *) func_append tmp_deplibs " $test_deplib" ;; esac done deplibs=$tmp_deplibs if test -n "$convenience"; then if test -n "$whole_archive_flag_spec" && test yes = "$compiler_needs_object" && test -z "$libobjs"; then # extract the archives, so we have objects to list. # TODO: could optimize this to just extract one archive. whole_archive_flag_spec= fi if test -n "$whole_archive_flag_spec"; then save_libobjs=$libobjs eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= else gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $convenience func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi fi if test yes = "$thread_safe" && test -n "$thread_safe_flag_spec"; then eval flag=\"$thread_safe_flag_spec\" func_append linker_flags " $flag" fi # Make a backup of the uninstalled library when relinking if test relink = "$opt_mode"; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? fi # Do each of the archive commands. if test yes = "$module" && test -n "$module_cmds"; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then eval test_cmds=\"$module_expsym_cmds\" cmds=$module_expsym_cmds else eval test_cmds=\"$module_cmds\" cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then eval test_cmds=\"$archive_expsym_cmds\" cmds=$archive_expsym_cmds else eval test_cmds=\"$archive_cmds\" cmds=$archive_cmds fi fi if test : != "$skipped_export" && func_len " $test_cmds" && len=$func_len_result && test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then : else # The command line is too long to link in one step, link piecewise # or, if using GNU ld and skipped_export is not :, use a linker # script. # Save the value of $output and $libobjs because we want to # use them later. If we have whole_archive_flag_spec, we # want to use save_libobjs as it was before # whole_archive_flag_spec was expanded, because we can't # assume the linker understands whole_archive_flag_spec. # This may have to be revisited, in case too many # convenience libraries get linked in and end up exceeding # the spec. if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then save_libobjs=$libobjs fi save_output=$output func_basename "$output" output_la=$func_basename_result # Clear the reloadable object creation command queue and # initialize k to one. test_cmds= concat_cmds= objlist= last_robj= k=1 if test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then output=$output_objdir/$output_la.lnkscript func_verbose "creating GNU ld script: $output" echo 'INPUT (' > $output for obj in $save_libobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done echo ')' >> $output func_append delfiles " $output" func_to_tool_file "$output" output=$func_to_tool_file_result elif test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then output=$output_objdir/$output_la.lnk func_verbose "creating linker input file list: $output" : > $output set x $save_libobjs shift firstobj= if test yes = "$compiler_needs_object"; then firstobj="$1 " shift fi for obj do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done func_append delfiles " $output" func_to_tool_file "$output" output=$firstobj\"$file_list_spec$func_to_tool_file_result\" else if test -n "$save_libobjs"; then func_verbose "creating reloadable object files..." output=$output_objdir/$output_la-$k.$objext eval test_cmds=\"$reload_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 # Loop over the list of objects to be linked. for obj in $save_libobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result if test -z "$objlist" || test "$len" -lt "$max_cmd_len"; then func_append objlist " $obj" else # The command $test_cmds is almost too long, add a # command to the queue. if test 1 -eq "$k"; then # The first file doesn't have a previous command to add. reload_objs=$objlist eval concat_cmds=\"$reload_cmds\" else # All subsequent reloadable object files will link in # the last one created. reload_objs="$objlist $last_robj" eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" fi last_robj=$output_objdir/$output_la-$k.$objext func_arith $k + 1 k=$func_arith_result output=$output_objdir/$output_la-$k.$objext objlist=" $obj" func_len " $last_robj" func_arith $len0 + $func_len_result len=$func_arith_result fi done # Handle the remaining objects by creating one last # reloadable object file. All subsequent reloadable object # files will link in the last one created. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ reload_objs="$objlist $last_robj" eval concat_cmds=\"\$concat_cmds$reload_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi func_append delfiles " $output" else output= fi ${skipped_export-false} && { func_verbose "generating symbol list for '$libname.la'" export_symbols=$output_objdir/$libname.exp $opt_dry_run || $RM $export_symbols libobjs=$output # Append the command to create the export file. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi } test -n "$save_libobjs" && func_verbose "creating a temporary reloadable object file: $output" # Loop through the commands generated above and execute them. save_ifs=$IFS; IFS='~' for cmd in $concat_cmds; do IFS=$save_ifs $opt_quiet || { func_quote_arg expand,pretty "$cmd" eval "func_echo $func_quote_arg_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test relink = "$opt_mode"; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS=$save_ifs if test -n "$export_symbols_regex" && ${skipped_export-false}; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi ${skipped_export-false} && { if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols=$export_symbols test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for '$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands, which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi } libobjs=$output # Restore the value of output. output=$save_output if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= fi # Expand the library linking commands again to reset the # value of $libobjs for piecewise linking. # Do each of the archive commands. if test yes = "$module" && test -n "$module_cmds"; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then cmds=$module_expsym_cmds else cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then cmds=$archive_expsym_cmds else cmds=$archive_cmds fi fi fi if test -n "$delfiles"; then # Append the command to remove temporary files to $cmds. eval cmds=\"\$cmds~\$RM $delfiles\" fi # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi save_ifs=$IFS; IFS='~' for cmd in $cmds; do IFS=$sp$nl eval cmd=\"$cmd\" IFS=$save_ifs $opt_quiet || { func_quote_arg expand,pretty "$cmd" eval "func_echo $func_quote_arg_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test relink = "$opt_mode"; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS=$save_ifs # Restore the uninstalled library and exit if test relink = "$opt_mode"; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? if test -n "$convenience"; then if test -z "$whole_archive_flag_spec"; then func_show_eval '${RM}r "$gentop"' fi fi exit $EXIT_SUCCESS fi # Create links to the real library. for linkname in $linknames; do if test "$realname" != "$linkname"; then func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' fi done # If -module or -export-dynamic was specified, set the dlname. if test yes = "$module" || test yes = "$export_dynamic"; then # On all known operating systems, these are identical. dlname=$soname fi fi ;; obj) if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then func_warning "'-dlopen' is ignored for objects" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "'-l' and '-L' are ignored for objects" ;; esac test -n "$rpath" && \ func_warning "'-rpath' is ignored for objects" test -n "$xrpath" && \ func_warning "'-R' is ignored for objects" test -n "$vinfo" && \ func_warning "'-version-info' is ignored for objects" test -n "$release" && \ func_warning "'-release' is ignored for objects" case $output in *.lo) test -n "$objs$old_deplibs" && \ func_fatal_error "cannot build library object '$output' from non-libtool objects" libobj=$output func_lo2o "$libobj" obj=$func_lo2o_result ;; *) libobj= obj=$output ;; esac # Delete the old objects. $opt_dry_run || $RM $obj $libobj # Objects from convenience libraries. This assumes # single-version convenience libraries. Whenever we create # different ones for PIC/non-PIC, this we'll have to duplicate # the extraction. reload_conv_objs= gentop= # if reload_cmds runs $LD directly, get rid of -Wl from # whole_archive_flag_spec and hope we can get by with turning comma # into space. case $reload_cmds in *\$LD[\ \$]*) wl= ;; esac if test -n "$convenience"; then if test -n "$whole_archive_flag_spec"; then eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" test -n "$wl" || tmp_whole_archive_flags=`$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` reload_conv_objs=$reload_objs\ $tmp_whole_archive_flags else gentop=$output_objdir/${obj}x func_append generated " $gentop" func_extract_archives $gentop $convenience reload_conv_objs="$reload_objs $func_extract_archives_result" fi fi # If we're not building shared, we need to use non_pic_objs test yes = "$build_libtool_libs" || libobjs=$non_pic_objects # Create the old-style object. reload_objs=$objs$old_deplibs' '`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; /\.lib$/d; $lo2o" | $NL2SP`' '$reload_conv_objs output=$obj func_execute_cmds "$reload_cmds" 'exit $?' # Exit if we aren't doing a library object file. if test -z "$libobj"; then if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS fi test yes = "$build_libtool_libs" || { if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi # Create an invalid libtool object if no PIC, so that we don't # accidentally link it into a program. # $show "echo timestamp > $libobj" # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? exit $EXIT_SUCCESS } if test -n "$pic_flag" || test default != "$pic_mode"; then # Only do commands if we really have different PIC objects. reload_objs="$libobjs $reload_conv_objs" output=$libobj func_execute_cmds "$reload_cmds" 'exit $?' fi if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS ;; prog) case $host in *cygwin*) func_stripname '' '.exe' "$output" output=$func_stripname_result.exe;; esac test -n "$vinfo" && \ func_warning "'-version-info' is ignored for programs" test -n "$release" && \ func_warning "'-release' is ignored for programs" $preload \ && test unknown,unknown,unknown = "$dlopen_support,$dlopen_self,$dlopen_self_static" \ && func_warning "'LT_INIT([dlopen])' not used. Assuming no dlopen support." case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library is the System framework compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac case $host in *-*-darwin*) # Don't allow lazy linking, it breaks C++ global constructors # But is supposedly fixed on 10.4 or later (yay!). if test CXX = "$tagname"; then case ${MACOSX_DEPLOYMENT_TARGET-10.0} in 10.[0123]) func_append compile_command " $wl-bind_at_load" func_append finalize_command " $wl-bind_at_load" ;; esac fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $compile_deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $compile_deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done compile_deplibs=$new_libs func_append compile_command " $compile_deplibs" func_append finalize_command " $finalize_deplibs" if test -n "$rpath$xrpath"; then # If the user specified any rpath flags, then add them. for libdir in $rpath $xrpath; do # This is the magic to use -rpath. case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done fi # Now hardcode the library paths rpath= hardcode_libdirs= for libdir in $compile_rpath $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "$libdir" | $SED -e 's*/lib$*/bin*'` case :$dllsearchpath: in *":$libdir:"*) ;; ::) dllsearchpath=$libdir;; *) func_append dllsearchpath ":$libdir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval rpath=\" $hardcode_libdir_flag_spec\" fi compile_rpath=$rpath rpath= hardcode_libdirs= for libdir in $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$finalize_perm_rpath " in *" $libdir "*) ;; *) func_append finalize_perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval rpath=\" $hardcode_libdir_flag_spec\" fi finalize_rpath=$rpath if test -n "$libobjs" && test yes = "$build_old_libs"; then # Transform all the library objects into standard objects. compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` fi func_generate_dlsyms "$outputname" "@PROGRAM@" false # template prelinking step if test -n "$prelink_cmds"; then func_execute_cmds "$prelink_cmds" 'exit $?' fi wrappers_required=: case $host in *cegcc* | *mingw32ce*) # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. wrappers_required=false ;; *cygwin* | *mingw* ) test yes = "$build_libtool_libs" || wrappers_required=false ;; *) if test no = "$need_relink" || test yes != "$build_libtool_libs"; then wrappers_required=false fi ;; esac $wrappers_required || { # Replace the output file specification. compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` link_command=$compile_command$compile_rpath # We have no uninstalled library dependencies, so finalize right now. exit_status=0 func_show_eval "$link_command" 'exit_status=$?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Delete the generated files. if test -f "$output_objdir/${outputname}S.$objext"; then func_show_eval '$RM "$output_objdir/${outputname}S.$objext"' fi exit $exit_status } if test -n "$compile_shlibpath$finalize_shlibpath"; then compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" fi if test -n "$finalize_shlibpath"; then finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" fi compile_var= finalize_var= if test -n "$runpath_var"; then if test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done compile_var="$runpath_var=\"$rpath\$$runpath_var\" " fi if test -n "$finalize_perm_rpath"; then # We should set the runpath_var. rpath= for dir in $finalize_perm_rpath; do func_append rpath "$dir:" done finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " fi fi if test yes = "$no_install"; then # We don't need to create a wrapper script. link_command=$compile_var$compile_command$compile_rpath # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` # Delete the old output file. $opt_dry_run || $RM $output # Link the executable and exit func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi exit $EXIT_SUCCESS fi case $hardcode_action,$fast_install in relink,*) # Fast installation is not supported link_command=$compile_var$compile_command$compile_rpath relink_command=$finalize_var$finalize_command$finalize_rpath func_warning "this platform does not like uninstalled shared libraries" func_warning "'$output' will be relinked during installation" ;; *,yes) link_command=$finalize_var$compile_command$finalize_rpath relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'` ;; *,no) link_command=$compile_var$compile_command$compile_rpath relink_command=$finalize_var$finalize_command$finalize_rpath ;; *,needless) link_command=$finalize_var$compile_command$finalize_rpath relink_command= ;; esac # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` # Delete the old output files. $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output_objdir/$outputname" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Now create the wrapper script. func_verbose "creating $output" # Quote the relink command for shipping. if test -n "$relink_command"; then # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_arg pretty "$var_value" relink_command="$var=$func_quote_arg_result; export $var; $relink_command" fi done func_quote eval cd "`pwd`" func_quote_arg pretty,unquoted "($func_quote_result; $relink_command)" relink_command=$func_quote_arg_unquoted_result fi # Only actually do things if not in dry run mode. $opt_dry_run || { # win32 will think the script is a binary if it has # a .exe suffix, so we strip it off here. case $output in *.exe) func_stripname '' '.exe' "$output" output=$func_stripname_result ;; esac # test for cygwin because mv fails w/o .exe extensions case $host in *cygwin*) exeext=.exe func_stripname '' '.exe' "$outputname" outputname=$func_stripname_result ;; *) exeext= ;; esac case $host in *cygwin* | *mingw* ) func_dirname_and_basename "$output" "" "." output_name=$func_basename_result output_path=$func_dirname_result cwrappersource=$output_path/$objdir/lt-$output_name.c cwrapper=$output_path/$output_name.exe $RM $cwrappersource $cwrapper trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 func_emit_cwrapperexe_src > $cwrappersource # The wrapper executable is built using the $host compiler, # because it contains $host paths and files. If cross- # compiling, it, like the target executable, must be # executed on the $host or under an emulation environment. $opt_dry_run || { $LTCC $LTCFLAGS -o $cwrapper $cwrappersource $STRIP $cwrapper } # Now, create the wrapper script for func_source use: func_ltwrapper_scriptname $cwrapper $RM $func_ltwrapper_scriptname_result trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 $opt_dry_run || { # note: this script will not be executed, so do not chmod. if test "x$build" = "x$host"; then $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result else func_emit_wrapper no > $func_ltwrapper_scriptname_result fi } ;; * ) $RM $output trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 func_emit_wrapper no > $output chmod +x $output ;; esac } exit $EXIT_SUCCESS ;; esac # See if we need to build an old-fashioned archive. for oldlib in $oldlibs; do case $build_libtool_libs in convenience) oldobjs="$libobjs_save $symfileobj" addlibs=$convenience build_libtool_libs=no ;; module) oldobjs=$libobjs_save addlibs=$old_convenience build_libtool_libs=no ;; *) oldobjs="$old_deplibs $non_pic_objects" $preload && test -f "$symfileobj" \ && func_append oldobjs " $symfileobj" addlibs=$old_convenience ;; esac if test -n "$addlibs"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $addlibs func_append oldobjs " $func_extract_archives_result" fi # Do each command in the archive commands. if test -n "$old_archive_from_new_cmds" && test yes = "$build_libtool_libs"; then cmds=$old_archive_from_new_cmds else # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append oldobjs " $func_extract_archives_result" fi # POSIX demands no paths to be encoded in archives. We have # to avoid creating archives with duplicate basenames if we # might have to extract them afterwards, e.g., when creating a # static archive out of a convenience library, or when linking # the entirety of a libtool archive into another (currently # not supported by libtool). if (for obj in $oldobjs do func_basename "$obj" $ECHO "$func_basename_result" done | sort | sort -uc >/dev/null 2>&1); then : else echo "copying selected object files to avoid basename conflicts..." gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_mkdir_p "$gentop" save_oldobjs=$oldobjs oldobjs= counter=1 for obj in $save_oldobjs do func_basename "$obj" objbase=$func_basename_result case " $oldobjs " in " ") oldobjs=$obj ;; *[\ /]"$objbase "*) while :; do # Make sure we don't pick an alternate name that also # overlaps. newobj=lt$counter-$objbase func_arith $counter + 1 counter=$func_arith_result case " $oldobjs " in *[\ /]"$newobj "*) ;; *) if test ! -f "$gentop/$newobj"; then break; fi ;; esac done func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" func_append oldobjs " $gentop/$newobj" ;; *) func_append oldobjs " $obj" ;; esac done fi func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result eval cmds=\"$old_archive_cmds\" func_len " $cmds" len=$func_len_result if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then cmds=$old_archive_cmds elif test -n "$archiver_list_spec"; then func_verbose "using command file archive linking..." for obj in $oldobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > $output_objdir/$libname.libcmd func_to_tool_file "$output_objdir/$libname.libcmd" oldobjs=" $archiver_list_spec$func_to_tool_file_result" cmds=$old_archive_cmds else # the command line is too long to link in one step, link in parts func_verbose "using piecewise archive linking..." save_RANLIB=$RANLIB RANLIB=: objlist= concat_cmds= save_oldobjs=$oldobjs oldobjs= # Is there a better way of finding the last object in the list? for obj in $save_oldobjs do last_oldobj=$obj done eval test_cmds=\"$old_archive_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 for obj in $save_oldobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result func_append objlist " $obj" if test "$len" -lt "$max_cmd_len"; then : else # the above command should be used before it gets too long oldobjs=$objlist if test "$obj" = "$last_oldobj"; then RANLIB=$save_RANLIB fi test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\$concat_cmds$old_archive_cmds\" objlist= len=$len0 fi done RANLIB=$save_RANLIB oldobjs=$objlist if test -z "$oldobjs"; then eval cmds=\"\$concat_cmds\" else eval cmds=\"\$concat_cmds~\$old_archive_cmds\" fi fi fi func_execute_cmds "$cmds" 'exit $?' done test -n "$generated" && \ func_show_eval "${RM}r$generated" # Now create the libtool archive. case $output in *.la) old_library= test yes = "$build_old_libs" && old_library=$libname.$libext func_verbose "creating $output" # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_arg pretty,unquoted "$var_value" relink_command="$var=$func_quote_arg_unquoted_result; export $var; $relink_command" fi done # Quote the link command for shipping. func_quote eval cd "`pwd`" relink_command="($func_quote_result; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" func_quote_arg pretty,unquoted "$relink_command" relink_command=$func_quote_arg_unquoted_result if test yes = "$hardcode_automatic"; then relink_command= fi # Only create the output if not a dry run. $opt_dry_run || { for installed in no yes; do if test yes = "$installed"; then if test -z "$install_libdir"; then break fi output=$output_objdir/${outputname}i # Replace all uninstalled libtool libraries with the installed ones newdependency_libs= for deplib in $dependency_libs; do case $deplib in *.la) func_basename "$deplib" name=$func_basename_result func_resolve_sysroot "$deplib" eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` test -z "$libdir" && \ func_fatal_error "'$deplib' is not a valid libtool archive" func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name" ;; -L*) func_stripname -L '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -L$func_replace_sysroot_result" ;; -R*) func_stripname -R '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -R$func_replace_sysroot_result" ;; *) func_append newdependency_libs " $deplib" ;; esac done dependency_libs=$newdependency_libs newdlfiles= for lib in $dlfiles; do case $lib in *.la) func_basename "$lib" name=$func_basename_result eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "'$lib' is not a valid libtool archive" func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name" ;; *) func_append newdlfiles " $lib" ;; esac done dlfiles=$newdlfiles newdlprefiles= for lib in $dlprefiles; do case $lib in *.la) # Only pass preopened files to the pseudo-archive (for # eventual linking with the app. that links it) if we # didn't already link the preopened objects directly into # the library: func_basename "$lib" name=$func_basename_result eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "'$lib' is not a valid libtool archive" func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name" ;; esac done dlprefiles=$newdlprefiles else newdlfiles= for lib in $dlfiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlfiles " $abs" done dlfiles=$newdlfiles newdlprefiles= for lib in $dlprefiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlprefiles " $abs" done dlprefiles=$newdlprefiles fi $RM $output # place dlname in correct position for cygwin # In fact, it would be nice if we could use this code for all target # systems that can't hard-code library paths into their executables # and that have no shared library path variable independent of PATH, # but it turns out we can't easily determine that from inspecting # libtool variables, so we have to hard-code the OSs to which it # applies here; at the moment, that means platforms that use the PE # object format with DLL files. See the long comment at the top of # tests/bindir.at for full details. tdlname=$dlname case $host,$output,$installed,$module,$dlname in *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) # If a -bindir argument was supplied, place the dll there. if test -n "$bindir"; then func_relative_path "$install_libdir" "$bindir" tdlname=$func_relative_path_result/$dlname else # Otherwise fall back on heuristic. tdlname=../bin/$dlname fi ;; esac $ECHO > $output "\ # $outputname - a libtool library file # Generated by $PROGRAM (GNU $PACKAGE) $VERSION # # Please DO NOT delete this file! # It is necessary for linking the library. # The name that we can dlopen(3). dlname='$tdlname' # Names of this library. library_names='$library_names' # The name of the static archive. old_library='$old_library' # Linker flags that cannot go in dependency_libs. inherited_linker_flags='$new_inherited_linker_flags' # Libraries that this one depends upon. dependency_libs='$dependency_libs' # Names of additional weak libraries provided by this library weak_library_names='$weak_libs' # Version information for $libname. current=$current age=$age revision=$revision # Is this an already installed library? installed=$installed # Should we warn about portability when linking against -modules? shouldnotlink=$module # Files to dlopen/dlpreopen dlopen='$dlfiles' dlpreopen='$dlprefiles' # Directory that this library needs to be installed in: libdir='$install_libdir'" if test no,yes = "$installed,$need_relink"; then $ECHO >> $output "\ relink_command=\"$relink_command\"" fi done } # Do a symbolic link so that the libtool archive can be found in # LD_LIBRARY_PATH before the program is installed. func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' ;; esac exit $EXIT_SUCCESS } if test link = "$opt_mode" || test relink = "$opt_mode"; then func_mode_link ${1+"$@"} fi # func_mode_uninstall arg... func_mode_uninstall () { $debug_cmd RM=$nonopt files= rmforce=false exit_status=0 # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic=$magic for arg do case $arg in -f) func_append RM " $arg"; rmforce=: ;; -*) func_append RM " $arg" ;; *) func_append files " $arg" ;; esac done test -z "$RM" && \ func_fatal_help "you must specify an RM program" rmdirs= for file in $files; do func_dirname "$file" "" "." dir=$func_dirname_result if test . = "$dir"; then odir=$objdir else odir=$dir/$objdir fi func_basename "$file" name=$func_basename_result test uninstall = "$opt_mode" && odir=$dir # Remember odir for removal later, being careful to avoid duplicates if test clean = "$opt_mode"; then case " $rmdirs " in *" $odir "*) ;; *) func_append rmdirs " $odir" ;; esac fi # Don't error if the file doesn't exist and rm -f was used. if { test -L "$file"; } >/dev/null 2>&1 || { test -h "$file"; } >/dev/null 2>&1 || test -f "$file"; then : elif test -d "$file"; then exit_status=1 continue elif $rmforce; then continue fi rmfiles=$file case $name in *.la) # Possibly a libtool archive, so verify it. if func_lalib_p "$file"; then func_source $dir/$name # Delete the libtool libraries and symlinks. for n in $library_names; do func_append rmfiles " $odir/$n" done test -n "$old_library" && func_append rmfiles " $odir/$old_library" case $opt_mode in clean) case " $library_names " in *" $dlname "*) ;; *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;; esac test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i" ;; uninstall) if test -n "$library_names"; then # Do each command in the postuninstall commands. func_execute_cmds "$postuninstall_cmds" '$rmforce || exit_status=1' fi if test -n "$old_library"; then # Do each command in the old_postuninstall commands. func_execute_cmds "$old_postuninstall_cmds" '$rmforce || exit_status=1' fi # FIXME: should reinstall the best remaining shared library. ;; esac fi ;; *.lo) # Possibly a libtool object, so verify it. if func_lalib_p "$file"; then # Read the .lo file func_source $dir/$name # Add PIC object to the list of files to remove. if test -n "$pic_object" && test none != "$pic_object"; then func_append rmfiles " $dir/$pic_object" fi # Add non-PIC object to the list of files to remove. if test -n "$non_pic_object" && test none != "$non_pic_object"; then func_append rmfiles " $dir/$non_pic_object" fi fi ;; *) if test clean = "$opt_mode"; then noexename=$name case $file in *.exe) func_stripname '' '.exe' "$file" file=$func_stripname_result func_stripname '' '.exe' "$name" noexename=$func_stripname_result # $file with .exe has already been added to rmfiles, # add $file without .exe func_append rmfiles " $file" ;; esac # Do a test to see if this is a libtool program. if func_ltwrapper_p "$file"; then if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" relink_command= func_source $func_ltwrapper_scriptname_result func_append rmfiles " $func_ltwrapper_scriptname_result" else relink_command= func_source $dir/$noexename fi # note $name still contains .exe if it was in $file originally # as does the version of $file that was added into $rmfiles func_append rmfiles " $odir/$name $odir/${name}S.$objext" if test yes = "$fast_install" && test -n "$relink_command"; then func_append rmfiles " $odir/lt-$name" fi if test "X$noexename" != "X$name"; then func_append rmfiles " $odir/lt-$noexename.c" fi fi fi ;; esac func_show_eval "$RM $rmfiles" 'exit_status=1' done # Try to remove the $objdir's in the directories where we deleted files for dir in $rmdirs; do if test -d "$dir"; then func_show_eval "rmdir $dir >/dev/null 2>&1" fi done exit $exit_status } if test uninstall = "$opt_mode" || test clean = "$opt_mode"; then func_mode_uninstall ${1+"$@"} fi test -z "$opt_mode" && { help=$generic_help func_fatal_help "you must specify a MODE" } test -z "$exec_cmd" && \ func_fatal_help "invalid operation mode '$opt_mode'" if test -n "$exec_cmd"; then eval exec "$exec_cmd" exit $EXIT_FAILURE fi exit $exit_status # The TAGs below are defined such that we never get into a situation # where we disable both kinds of libraries. Given conflicting # choices, we go for a static library, that is the most portable, # since we can't tell whether shared libraries were disabled because # the user asked for that or because the platform doesn't support # them. This is particularly important on AIX, because we don't # support having both static and shared libraries enabled at the same # time on that platform, so we default to a shared-only configuration. # If a disable-shared tag is given, we'll fallback to a static-only # configuration. But we'll never go from static-only to shared-only. # ### BEGIN LIBTOOL TAG CONFIG: disable-shared build_libtool_libs=no build_old_libs=yes # ### END LIBTOOL TAG CONFIG: disable-shared # ### BEGIN LIBTOOL TAG CONFIG: disable-static build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` # ### END LIBTOOL TAG CONFIG: disable-static # Local Variables: # mode:shell-script # sh-indentation:2 # End: wimlib-1.13.1/build-aux/nasm_lt.sh0000744000175000017500000000306213016523676013677 00000000000000#! /bin/sh command="" infile="" o_opt=no pic=no while [ $# -gt 0 ]; do case "$1" in -DPIC|-fPIC|-fpic|-Kpic|-KPIC) if [ "$pic" != "yes" ] ; then command="$command -DPIC" pic=yes fi ;; -f|-fbin|-faout|-faoutb|-fcoff|-felf|-felf64|-fas86| \ -fobj|-fwin32|-fwin64|-frdf|-fieee|-fmacho|-fmacho64) # it's a file format specifier for nasm. command="$command $1" ;; -f*) # maybe a code-generation flag for gcc. ;; -[Ii]*) incdir=`echo "$1" | sed 's/^-[Ii]//'` if [ "x$incdir" = x -a "x$2" != x ] ; then case "$2" in -*) ;; *) incdir="$2"; shift;; esac fi if [ "x$incdir" != x ] ; then # In the case of NASM, the trailing slash is necessary. incdir=`echo "$incdir" | sed 's%/*$%/%'` command="$command -I$incdir" fi ;; -o*) o_opt=yes command="$command $1" ;; *.asm) infile=$1 command="$command $1" ;; *) command="$command $1" ;; esac shift done if [ "$o_opt" != yes ] ; then # By default, NASM creates an output file # in the same directory as the input file. outfile="-o `echo $infile | sed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.o" command="$command $outfile" fi echo $command exec $command wimlib-1.13.1/build-aux/test-driver0000744000175000017500000001104213427652377014105 00000000000000#! /bin/sh # test-driver - basic testsuite driver script. scriptversion=2018-03-07.03; # UTC # Copyright (C) 2011-2018 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 Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . # Make unconditional expansion of undefined variables an error. This # helps a lot in preventing typo-related bugs. set -u usage_error () { echo "$0: $*" >&2 print_usage >&2 exit 2 } print_usage () { cat <$log_file 2>&1 estatus=$? if test $enable_hard_errors = no && test $estatus -eq 99; then tweaked_estatus=1 else tweaked_estatus=$estatus fi case $tweaked_estatus:$expect_failure in 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; 0:*) col=$grn res=PASS recheck=no gcopy=no;; 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; *:*) col=$red res=FAIL recheck=yes gcopy=yes;; esac # Report the test outcome and exit status in the logs, so that one can # know whether the test passed or failed simply by looking at the '.log' # file, without the need of also peaking into the corresponding '.trs' # file (automake bug#11814). echo "$res $test_name (exit status: $estatus)" >>$log_file # Report outcome to console. echo "${col}${res}${std}: $test_name" # Register the test result, and other relevant metadata. echo ":test-result: $res" > $trs_file echo ":global-test-result: $res" >> $trs_file echo ":recheck: $recheck" >> $trs_file echo ":copy-in-global-log: $gcopy" >> $trs_file # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: wimlib-1.13.1/build-aux/ar-lib0000744000175000017500000001330313427652377013005 00000000000000#! /bin/sh # Wrapper for Microsoft lib.exe me=ar-lib scriptversion=2012-03-01.08; # UTC # Copyright (C) 2010-2018 Free Software Foundation, Inc. # Written by Peter Rosin . # # 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 Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . # func_error message func_error () { echo "$me: $1" 1>&2 exit 1 } file_conv= # func_file_conv build_file # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv in mingw) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin) file=`cygpath -m "$file" || echo "$file"` ;; wine) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_at_file at_file operation archive # Iterate over all members in AT_FILE performing OPERATION on ARCHIVE # for each of them. # When interpreting the content of the @FILE, do NOT use func_file_conv, # since the user would need to supply preconverted file names to # binutils ar, at least for MinGW. func_at_file () { operation=$2 archive=$3 at_file_contents=`cat "$1"` eval set x "$at_file_contents" shift for member do $AR -NOLOGO $operation:"$member" "$archive" || exit $? done } case $1 in '') func_error "no command. Try '$0 --help' for more information." ;; -h | --h*) cat <&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) is_target_a_directory=always dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) is_target_a_directory=never;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done # We allow the use of options -d and -T together, by making -d # take the precedence; this is for compatibility with GNU install. if test -n "$dir_arg"; then if test -n "$dst_arg"; then echo "$0: target directory not allowed when installing a directory." >&2 exit 1 fi fi if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then if test $# -gt 1 || test "$is_target_a_directory" = always; then if test ! -d "$dst_arg"; then echo "$0: $dst_arg: Is not a directory." >&2 exit 1 fi fi fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dstbase=`basename "$src"` case $dst in */) dst=$dst$dstbase;; *) dst=$dst/$dstbase;; esac dstdir_status=0 else dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi case $dstdir in */) dstdirslash=$dstdir;; *) dstdirslash=$dstdir/;; esac obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) # Note that $RANDOM variable is not portable (e.g. dash); Use it # here however when possible just to lower collision chance. tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0 # Because "mkdir -p" follows existing symlinks and we likely work # directly in world-writeable /tmp, make sure that the '$tmpdir' # directory is successfully created first before we actually test # 'mkdir -p' feature. if (umask $mkdir_umask && $mkdirprog $mkdir_mode "$tmpdir" && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. test_tmpdir="$tmpdir/a" ls_ld_tmpdir=`ls -ld "$test_tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac oIFS=$IFS IFS=/ set -f set fnord $dstdir shift set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=${dstdirslash}_inst.$$_ rmtmp=${dstdirslash}_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: wimlib-1.13.1/build-aux/compile0000744000175000017500000001632713427652377013300 00000000000000#! /bin/sh # Wrapper for compilers which do not understand '-c -o'. scriptversion=2018-03-07.03; # UTC # Copyright (C) 1999-2018 Free Software Foundation, Inc. # Written by Tom Tromey . # # 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 Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' # We need space, tab and new line, in precisely that order. Quoting is # there to prevent tools from complaining about whitespace usage. IFS=" "" $nl" file_conv= # func_file_conv build_file lazy # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. If the determined conversion # type is listed in (the comma separated) LAZY, no conversion will # take place. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv/,$2, in *,$file_conv,*) ;; mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_cl_dashL linkdir # Make cl look for libraries in LINKDIR func_cl_dashL () { func_file_conv "$1" if test -z "$lib_path"; then lib_path=$file else lib_path="$lib_path;$file" fi linker_opts="$linker_opts -LIBPATH:$file" } # func_cl_dashl library # Do a library search-path lookup for cl func_cl_dashl () { lib=$1 found=no save_IFS=$IFS IFS=';' for dir in $lib_path $LIB do IFS=$save_IFS if $shared && test -f "$dir/$lib.dll.lib"; then found=yes lib=$dir/$lib.dll.lib break fi if test -f "$dir/$lib.lib"; then found=yes lib=$dir/$lib.lib break fi if test -f "$dir/lib$lib.a"; then found=yes lib=$dir/lib$lib.a break fi done IFS=$save_IFS if test "$found" != yes; then lib=$lib.lib fi } # func_cl_wrapper cl arg... # Adjust compile command to suit cl func_cl_wrapper () { # Assume a capable shell lib_path= shared=: linker_opts= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. eat=1 case $2 in *.o | *.[oO][bB][jJ]) func_file_conv "$2" set x "$@" -Fo"$file" shift ;; *) func_file_conv "$2" set x "$@" -Fe"$file" shift ;; esac ;; -I) eat=1 func_file_conv "$2" mingw set x "$@" -I"$file" shift ;; -I*) func_file_conv "${1#-I}" mingw set x "$@" -I"$file" shift ;; -l) eat=1 func_cl_dashl "$2" set x "$@" "$lib" shift ;; -l*) func_cl_dashl "${1#-l}" set x "$@" "$lib" shift ;; -L) eat=1 func_cl_dashL "$2" ;; -L*) func_cl_dashL "${1#-L}" ;; -static) shared=false ;; -Wl,*) arg=${1#-Wl,} save_ifs="$IFS"; IFS=',' for flag in $arg; do IFS="$save_ifs" linker_opts="$linker_opts $flag" done IFS="$save_ifs" ;; -Xlinker) eat=1 linker_opts="$linker_opts $2" ;; -*) set x "$@" "$1" shift ;; *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) func_file_conv "$1" set x "$@" -Tp"$file" shift ;; *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) func_file_conv "$1" mingw set x "$@" "$file" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -n "$linker_opts"; then linker_opts="-link$linker_opts" fi exec "$@" $linker_opts exit 1 } eat= case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: compile [--help] [--version] PROGRAM [ARGS] Wrapper for compilers which do not understand '-c -o'. Remove '-o dest.o' from ARGS, run PROGRAM with the remaining arguments, and rename the output as expected. If you are trying to build a whole package this is not the right script to run: please start by reading the file 'INSTALL'. Report bugs to . EOF exit $? ;; -v | --v*) echo "compile $scriptversion" exit $? ;; cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \ icl | *[/\\]icl | icl.exe | *[/\\]icl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac ofile= cfile= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. # So we strip '-o arg' only if arg is an object. eat=1 case $2 in *.o | *.obj) ofile=$2 ;; *) set x "$@" -o "$2" shift ;; esac ;; *.c) cfile=$1 set x "$@" "$1" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -z "$ofile" || test -z "$cfile"; then # If no '-o' option was seen then we might have been invoked from a # pattern rule where we don't need one. That is ok -- this is a # normal compilation that the losing compiler can handle. If no # '.c' file was seen then we are probably linking. That is also # ok. exec "$@" fi # Name of file we expect compiler to create. cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` # Create the lock directory. # Note: use '[/\\:.-]' here to ensure that we don't use the same name # that we are using for the .o file. Also, base the name on the expected # object file name, since that is what matters with a parallel build. lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d while true; do if mkdir "$lockdir" >/dev/null 2>&1; then break fi sleep 1 done # FIXME: race condition here if user kills between mkdir and trap. trap "rmdir '$lockdir'; exit 1" 1 2 15 # Run the compile. "$@" ret=$? if test -f "$cofile"; then test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" fi rmdir "$lockdir" exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: wimlib-1.13.1/build-aux/missing0000744000175000017500000001533613427652377013320 00000000000000#! /bin/sh # Common wrapper for a few potentially missing GNU programs. scriptversion=2018-03-07.03; # UTC # Copyright (C) 1996-2018 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # 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 Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try '$0 --help' for more information" exit 1 fi case $1 in --is-lightweight) # Used by our autoconf macros to check whether the available missing # script is modern enough. exit 0 ;; --run) # Back-compat with the calling convention used by older automake. shift ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit Supported PROGRAM values: aclocal autoconf autoheader autom4te automake makeinfo bison yacc flex lex help2man Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 'g' are ignored when checking the name. Send bug reports to ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing $scriptversion (GNU Automake)" exit $? ;; -*) echo 1>&2 "$0: unknown '$1' option" echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac # Run the given program, remember its exit status. "$@"; st=$? # If it succeeded, we are done. test $st -eq 0 && exit 0 # Also exit now if we it failed (or wasn't found), and '--version' was # passed; such an option is passed most likely to detect whether the # program is present and works. case $2 in --version|--help) exit $st;; esac # Exit code 63 means version mismatch. This often happens when the user # tries to use an ancient version of a tool on a file that requires a # minimum version. if test $st -eq 63; then msg="probably too old" elif test $st -eq 127; then # Program was missing. msg="missing on your system" else # Program was found and executed, but failed. Give up. exit $st fi perl_URL=https://www.perl.org/ flex_URL=https://github.com/westes/flex gnu_software_URL=https://www.gnu.org/software program_details () { case $1 in aclocal|automake) echo "The '$1' program is part of the GNU Automake package:" echo "<$gnu_software_URL/automake>" echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/autoconf>" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; autoconf|autom4te|autoheader) echo "The '$1' program is part of the GNU Autoconf package:" echo "<$gnu_software_URL/autoconf/>" echo "It also requires GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; esac } give_advice () { # Normalize program name to check for. normalized_program=`echo "$1" | sed ' s/^gnu-//; t s/^gnu//; t s/^g//; t'` printf '%s\n' "'$1' is $msg." configure_deps="'configure.ac' or m4 files included by 'configure.ac'" case $normalized_program in autoconf*) echo "You should only need it if you modified 'configure.ac'," echo "or m4 files included by it." program_details 'autoconf' ;; autoheader*) echo "You should only need it if you modified 'acconfig.h' or" echo "$configure_deps." program_details 'autoheader' ;; automake*) echo "You should only need it if you modified 'Makefile.am' or" echo "$configure_deps." program_details 'automake' ;; aclocal*) echo "You should only need it if you modified 'acinclude.m4' or" echo "$configure_deps." program_details 'aclocal' ;; autom4te*) echo "You might have modified some maintainer files that require" echo "the 'autom4te' program to be rebuilt." program_details 'autom4te' ;; bison*|yacc*) echo "You should only need it if you modified a '.y' file." echo "You may want to install the GNU Bison package:" echo "<$gnu_software_URL/bison/>" ;; lex*|flex*) echo "You should only need it if you modified a '.l' file." echo "You may want to install the Fast Lexical Analyzer package:" echo "<$flex_URL>" ;; help2man*) echo "You should only need it if you modified a dependency" \ "of a man page." echo "You may want to install the GNU Help2man package:" echo "<$gnu_software_URL/help2man/>" ;; makeinfo*) echo "You should only need it if you modified a '.texi' file, or" echo "any other file indirectly affecting the aspect of the manual." echo "You might want to install the Texinfo package:" echo "<$gnu_software_URL/texinfo/>" echo "The spurious makeinfo call might also be the consequence of" echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" echo "want to install GNU make:" echo "<$gnu_software_URL/make/>" ;; *) echo "You might have modified some files without having the proper" echo "tools for further handling them. Check the 'README' file, it" echo "often tells you about the needed prerequisites for installing" echo "this package. You may also peek at any GNU archive site, in" echo "case some other package contains this missing '$1' program." ;; esac } give_advice "$1" | sed -e '1s/^/WARNING: /' \ -e '2,$s/^/ /' >&2 # Propagate the correct exit status (expected to be 127 for a program # not found, 63 for a program that failed due to version mismatch). exit $st # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: wimlib-1.13.1/m4/0000755000175000017500000000000013464166632010414 500000000000000wimlib-1.13.1/m4/nasm.m40000644000175000017500000001161713016523676011540 00000000000000# AC_PROG_NASM # -------------------------- # Check that NASM exists and determine flags AC_DEFUN([AC_PROG_NASM],[ AC_CHECK_PROGS(NASM, [nasm nasmw yasm]) test -z "$NASM" && AC_MSG_ERROR([no nasm (Netwide Assembler) found]) AC_MSG_CHECKING([for object file format of host system]) case "$host_os" in cygwin* | mingw* | pw32* | interix*) case "$host_cpu" in x86_64) objfmt='Win64-COFF' ;; *) objfmt='Win32-COFF' ;; esac ;; msdosdjgpp* | go32*) objfmt='COFF' ;; os2-emx*) # not tested objfmt='MSOMF' # obj ;; linux*coff* | linux*oldld*) objfmt='COFF' # ??? ;; linux*aout*) objfmt='a.out' ;; linux*) case "$host_cpu" in x86_64) objfmt='ELF64' ;; *) objfmt='ELF' ;; esac ;; kfreebsd* | freebsd* | netbsd* | openbsd*) if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then objfmt='BSD-a.out' else case "$host_cpu" in x86_64 | amd64) objfmt='ELF64' ;; *) objfmt='ELF' ;; esac fi ;; solaris* | sunos* | sysv* | sco*) case "$host_cpu" in x86_64) objfmt='ELF64' ;; *) objfmt='ELF' ;; esac ;; darwin* | rhapsody* | nextstep* | openstep* | macos*) case "$host_cpu" in x86_64) objfmt='Mach-O64' ;; *) objfmt='Mach-O' ;; esac ;; *) objfmt='ELF ?' ;; esac AC_MSG_RESULT([$objfmt]) if test "$objfmt" = 'ELF ?'; then objfmt='ELF' AC_MSG_WARN([unexpected host system. assumed that the format is $objfmt.]) fi AC_MSG_CHECKING([for object file format specifier (NAFLAGS) ]) case "$objfmt" in MSOMF) NAFLAGS='-fobj -DOBJ32';; Win32-COFF) NAFLAGS='-fwin32 -DWIN32';; Win64-COFF) NAFLAGS='-fwin64 -DWIN64 -D__x86_64__';; COFF) NAFLAGS='-fcoff -DCOFF';; a.out) NAFLAGS='-faout -DAOUT';; BSD-a.out) NAFLAGS='-faoutb -DAOUT';; ELF) NAFLAGS='-felf -DELF';; ELF64) NAFLAGS='-felf64 -DELF -D__x86_64__';; RDF) NAFLAGS='-frdf -DRDF';; Mach-O) NAFLAGS='-fmacho -DMACHO';; Mach-O64) NAFLAGS='-fmacho64 -DMACHO -D__x86_64__';; esac AC_MSG_RESULT([$NAFLAGS]) AC_SUBST([NAFLAGS]) AC_MSG_CHECKING([whether the assembler ($NASM $NAFLAGS) works]) cat > conftest.asm <&AC_FD_CC cat conftest.asm >&AC_FD_CC rm -rf conftest* AC_MSG_RESULT(no) AC_MSG_ERROR([installation or configuration problem: assembler cannot create object files.]) fi AC_MSG_CHECKING([whether the linker accepts assembler output]) try_nasm='${CC-cc} -o conftest${ac_exeext} $LDFLAGS conftest.o $LIBS 1>&AC_FD_CC' if AC_TRY_EVAL(try_nasm) && test -s conftest${ac_exeext}; then rm -rf conftest* AC_MSG_RESULT(yes) else rm -rf conftest* AC_MSG_RESULT(no) AC_MSG_ERROR([configuration problem: maybe object file format mismatch.]) fi ]) # AC_CHECK_COMPATIBLE_ARM_ASSEMBLER_IFELSE # -------------------------- # Test whether the assembler is suitable and supports NEON instructions AC_DEFUN([AC_CHECK_COMPATIBLE_ARM_ASSEMBLER_IFELSE],[ ac_good_gnu_arm_assembler=no ac_save_CC="$CC" ac_save_CFLAGS="$CFLAGS" CFLAGS="$CCASFLAGS -x assembler-with-cpp" CC="$CCAS" AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ .text .fpu neon .arch armv7a .object_arch armv4 .arm pld [r0] vmovn.u16 d0, q0]])], ac_good_gnu_arm_assembler=yes) ac_use_gas_preprocessor=no if test "x$ac_good_gnu_arm_assembler" = "xno" ; then CC="gas-preprocessor.pl $CCAS" AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ .text .fpu neon .arch armv7a .object_arch armv4 .arm pld [r0] vmovn.u16 d0, q0]])], ac_use_gas_preprocessor=yes) fi CFLAGS="$ac_save_CFLAGS" CC="$ac_save_CC" if test "x$ac_use_gas_preprocessor" = "xyes" ; then CCAS="gas-preprocessor.pl $CCAS" AC_SUBST([CCAS]) ac_good_gnu_arm_assembler=yes fi if test "x$ac_good_gnu_arm_assembler" = "xyes" ; then $1 else $2 fi ]) # AC_CHECK_COMPATIBLE_MIPSEL_ASSEMBLER_IFELSE # -------------------------- # Test whether the assembler is suitable and supports MIPS instructions AC_DEFUN([AC_CHECK_COMPATIBLE_MIPSEL_ASSEMBLER_IFELSE],[ have_mips_dspr2=no ac_save_CFLAGS="$CFLAGS" CFLAGS="$CCASFLAGS -mdspr2" AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ int main () { int c = 0, a = 0, b = 0; __asm__ __volatile__ ( "precr.qb.ph %[c], %[a], %[b] \n\t" : [c] "=r" (c) : [a] "r" (a), [b] "r" (b) ); return c; } ]])], have_mips_dspr2=yes) CFLAGS=$ac_save_CFLAGS if test "x$have_mips_dspr2" = "xyes" ; then $1 else $2 fi ]) wimlib-1.13.1/m4/libtool.m40000644000175000017500000112732513427652374012257 00000000000000# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- # # Copyright (C) 1996-2001, 2003-2018 Free Software Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. m4_define([_LT_COPYING], [dnl # Copyright (C) 2014 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program or library that is built # using GNU Libtool, you may include this file under the same # distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . ]) # serial 58 LT_INIT # LT_PREREQ(VERSION) # ------------------ # Complain and exit if this libtool version is less that VERSION. m4_defun([LT_PREREQ], [m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, [m4_default([$3], [m4_fatal([Libtool version $1 or higher is required], 63)])], [$2])]) # _LT_CHECK_BUILDDIR # ------------------ # Complain if the absolute build directory name contains unusual characters m4_defun([_LT_CHECK_BUILDDIR], [case `pwd` in *\ * | *\ *) AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; esac ]) # LT_INIT([OPTIONS]) # ------------------ AC_DEFUN([LT_INIT], [AC_PREREQ([2.62])dnl We use AC_PATH_PROGS_FEATURE_CHECK AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl AC_BEFORE([$0], [LT_LANG])dnl AC_BEFORE([$0], [LT_OUTPUT])dnl AC_BEFORE([$0], [LTDL_INIT])dnl m4_require([_LT_CHECK_BUILDDIR])dnl dnl Autoconf doesn't catch unexpanded LT_ macros by default: m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 dnl unless we require an AC_DEFUNed macro: AC_REQUIRE([LTOPTIONS_VERSION])dnl AC_REQUIRE([LTSUGAR_VERSION])dnl AC_REQUIRE([LTVERSION_VERSION])dnl AC_REQUIRE([LTOBSOLETE_VERSION])dnl m4_require([_LT_PROG_LTMAIN])dnl _LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}]) dnl Parse OPTIONS _LT_SET_OPTIONS([$0], [$1]) # This can be used to rebuild libtool when needed LIBTOOL_DEPS=$ltmain # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' AC_SUBST(LIBTOOL)dnl _LT_SETUP # Only expand once: m4_define([LT_INIT]) ])# LT_INIT # Old names: AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PROG_LIBTOOL], []) dnl AC_DEFUN([AM_PROG_LIBTOOL], []) # _LT_PREPARE_CC_BASENAME # ----------------------- m4_defun([_LT_PREPARE_CC_BASENAME], [ # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in @S|@*""; do case $cc_temp in compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } ])# _LT_PREPARE_CC_BASENAME # _LT_CC_BASENAME(CC) # ------------------- # It would be clearer to call AC_REQUIREs from _LT_PREPARE_CC_BASENAME, # but that macro is also expanded into generated libtool script, which # arranges for $SED and $ECHO to be set by different means. m4_defun([_LT_CC_BASENAME], [m4_require([_LT_PREPARE_CC_BASENAME])dnl AC_REQUIRE([_LT_DECL_SED])dnl AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl func_cc_basename $1 cc_basename=$func_cc_basename_result ]) # _LT_FILEUTILS_DEFAULTS # ---------------------- # It is okay to use these file commands and assume they have been set # sensibly after 'm4_require([_LT_FILEUTILS_DEFAULTS])'. m4_defun([_LT_FILEUTILS_DEFAULTS], [: ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} ])# _LT_FILEUTILS_DEFAULTS # _LT_SETUP # --------- m4_defun([_LT_SETUP], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl _LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl dnl _LT_DECL([], [host_alias], [0], [The host system])dnl _LT_DECL([], [host], [0])dnl _LT_DECL([], [host_os], [0])dnl dnl _LT_DECL([], [build_alias], [0], [The build system])dnl _LT_DECL([], [build], [0])dnl _LT_DECL([], [build_os], [0])dnl dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl dnl AC_REQUIRE([AC_PROG_LN_S])dnl test -z "$LN_S" && LN_S="ln -s" _LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl dnl AC_REQUIRE([LT_CMD_MAX_LEN])dnl _LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl _LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl m4_require([_LT_CMD_RELOAD])dnl m4_require([_LT_CHECK_MAGIC_METHOD])dnl m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl m4_require([_LT_CMD_OLD_ARCHIVE])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_WITH_SYSROOT])dnl m4_require([_LT_CMD_TRUNCATE])dnl _LT_CONFIG_LIBTOOL_INIT([ # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi ]) if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi _LT_CHECK_OBJDIR m4_require([_LT_TAG_COMPILER])dnl case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a '.a' archive for static linking (except MSVC and # ICC, which need '.lib'). libext=a with_gnu_ld=$lt_cv_prog_gnu_ld old_CC=$CC old_CFLAGS=$CFLAGS # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o _LT_CC_BASENAME([$compiler]) # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then _LT_PATH_MAGIC fi ;; esac # Use C for the default configuration in the libtool script LT_SUPPORTED_TAG([CC]) _LT_LANG_C_CONFIG _LT_LANG_DEFAULT_CONFIG _LT_CONFIG_COMMANDS ])# _LT_SETUP # _LT_PREPARE_SED_QUOTE_VARS # -------------------------- # Define a few sed substitution that help us do robust quoting. m4_defun([_LT_PREPARE_SED_QUOTE_VARS], [# Backslashify metacharacters that are still active within # double-quoted strings. sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\([["`\\]]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' ]) # _LT_PROG_LTMAIN # --------------- # Note that this code is called both from 'configure', and 'config.status' # now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, # 'config.status' has no value for ac_aux_dir unless we are using Automake, # so we pass a copy along to make sure it has a sensible value anyway. m4_defun([_LT_PROG_LTMAIN], [m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl _LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) ltmain=$ac_aux_dir/ltmain.sh ])# _LT_PROG_LTMAIN ## ------------------------------------- ## ## Accumulate code for creating libtool. ## ## ------------------------------------- ## # So that we can recreate a full libtool script including additional # tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS # in macros and then make a single call at the end using the 'libtool' # label. # _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) # ---------------------------------------- # Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL_INIT], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_INIT], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_INIT]) # _LT_CONFIG_LIBTOOL([COMMANDS]) # ------------------------------ # Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) # _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) # ----------------------------------------------------- m4_defun([_LT_CONFIG_SAVE_COMMANDS], [_LT_CONFIG_LIBTOOL([$1]) _LT_CONFIG_LIBTOOL_INIT([$2]) ]) # _LT_FORMAT_COMMENT([COMMENT]) # ----------------------------- # Add leading comment marks to the start of each line, and a trailing # full-stop to the whole comment if one is not present already. m4_define([_LT_FORMAT_COMMENT], [m4_ifval([$1], [ m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) )]) ## ------------------------ ## ## FIXME: Eliminate VARNAME ## ## ------------------------ ## # _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) # ------------------------------------------------------------------- # CONFIGNAME is the name given to the value in the libtool script. # VARNAME is the (base) name used in the configure script. # VALUE may be 0, 1 or 2 for a computed quote escaped value based on # VARNAME. Any other value will be used directly. m4_define([_LT_DECL], [lt_if_append_uniq([lt_decl_varnames], [$2], [, ], [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], [m4_ifval([$1], [$1], [$2])]) lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) m4_ifval([$4], [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) lt_dict_add_subkey([lt_decl_dict], [$2], [tagged?], [m4_ifval([$5], [yes], [no])])]) ]) # _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) # -------------------------------------------------------- m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) # lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_tag_varnames], [_lt_decl_filter([tagged?], [yes], $@)]) # _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) # --------------------------------------------------------- m4_define([_lt_decl_filter], [m4_case([$#], [0], [m4_fatal([$0: too few arguments: $#])], [1], [m4_fatal([$0: too few arguments: $#: $1])], [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], [lt_dict_filter([lt_decl_dict], $@)])[]dnl ]) # lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) # -------------------------------------------------- m4_define([lt_decl_quote_varnames], [_lt_decl_filter([value], [1], $@)]) # lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_dquote_varnames], [_lt_decl_filter([value], [2], $@)]) # lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_varnames_tagged], [m4_assert([$# <= 2])dnl _$0(m4_quote(m4_default([$1], [[, ]])), m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) m4_define([_lt_decl_varnames_tagged], [m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) # lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_all_varnames], [_$0(m4_quote(m4_default([$1], [[, ]])), m4_if([$2], [], m4_quote(lt_decl_varnames), m4_quote(m4_shift($@))))[]dnl ]) m4_define([_lt_decl_all_varnames], [lt_join($@, lt_decl_varnames_tagged([$1], lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl ]) # _LT_CONFIG_STATUS_DECLARE([VARNAME]) # ------------------------------------ # Quote a variable value, and forward it to 'config.status' so that its # declaration there will have the same value as in 'configure'. VARNAME # must have a single quote delimited value for this to work. m4_define([_LT_CONFIG_STATUS_DECLARE], [$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`']) # _LT_CONFIG_STATUS_DECLARATIONS # ------------------------------ # We delimit libtool config variables with single quotes, so when # we write them to config.status, we have to be sure to quote all # embedded single quotes properly. In configure, this macro expands # each variable declared with _LT_DECL (and _LT_TAGDECL) into: # # ='`$ECHO "$" | $SED "$delay_single_quote_subst"`' m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], [m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAGS # ---------------- # Output comment and list of tags supported by the script m4_defun([_LT_LIBTOOL_TAGS], [_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl available_tags='_LT_TAGS'dnl ]) # _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) # ----------------------------------- # Extract the dictionary values for VARNAME (optionally with TAG) and # expand to a commented shell variable setting: # # # Some comment about what VAR is for. # visible_name=$lt_internal_name m4_define([_LT_LIBTOOL_DECLARE], [_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [description])))[]dnl m4_pushdef([_libtool_name], m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), [0], [_libtool_name=[$]$1], [1], [_libtool_name=$lt_[]$1], [2], [_libtool_name=$lt_[]$1], [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl ]) # _LT_LIBTOOL_CONFIG_VARS # ----------------------- # Produce commented declarations of non-tagged libtool config variables # suitable for insertion in the LIBTOOL CONFIG section of the 'libtool' # script. Tagged libtool config variables (even for the LIBTOOL CONFIG # section) are produced by _LT_LIBTOOL_TAG_VARS. m4_defun([_LT_LIBTOOL_CONFIG_VARS], [m4_foreach([_lt_var], m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAG_VARS(TAG) # ------------------------- m4_define([_LT_LIBTOOL_TAG_VARS], [m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) # _LT_TAGVAR(VARNAME, [TAGNAME]) # ------------------------------ m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) # _LT_CONFIG_COMMANDS # ------------------- # Send accumulated output to $CONFIG_STATUS. Thanks to the lists of # variables for single and double quote escaping we saved from calls # to _LT_DECL, we can put quote escaped variables declarations # into 'config.status', and then the shell code to quote escape them in # for loops in 'config.status'. Finally, any additional code accumulated # from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. m4_defun([_LT_CONFIG_COMMANDS], [AC_PROVIDE_IFELSE([LT_OUTPUT], dnl If the libtool generation code has been placed in $CONFIG_LT, dnl instead of duplicating it all over again into config.status, dnl then we will have config.status run $CONFIG_LT later, so it dnl needs to know what name is stored there: [AC_CONFIG_COMMANDS([libtool], [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], dnl If the libtool generation code is destined for config.status, dnl expand the accumulated commands and init code now: [AC_CONFIG_COMMANDS([libtool], [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) ])#_LT_CONFIG_COMMANDS # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], [ # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' _LT_CONFIG_STATUS_DECLARATIONS LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$[]1 _LTECHO_EOF' } # Quote evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_quote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_dquote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done _LT_OUTPUT_LIBTOOL_INIT ]) # _LT_GENERATED_FILE_INIT(FILE, [COMMENT]) # ------------------------------------ # Generate a child script FILE with all initialization necessary to # reuse the environment learned by the parent script, and make the # file executable. If COMMENT is supplied, it is inserted after the # '#!' sequence but before initialization text begins. After this # macro, additional text can be appended to FILE to form the body of # the child script. The macro ends with non-zero status if the # file could not be fully written (such as if the disk is full). m4_ifdef([AS_INIT_GENERATED], [m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])], [m4_defun([_LT_GENERATED_FILE_INIT], [m4_require([AS_PREPARE])]dnl [m4_pushdef([AS_MESSAGE_LOG_FD])]dnl [lt_write_fail=0 cat >$1 <<_ASEOF || lt_write_fail=1 #! $SHELL # Generated by $as_me. $2 SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$1 <<\_ASEOF || lt_write_fail=1 AS_SHELL_SANITIZE _AS_PREPARE exec AS_MESSAGE_FD>&1 _ASEOF test 0 = "$lt_write_fail" && chmod +x $1[]dnl m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT # LT_OUTPUT # --------- # This macro allows early generation of the libtool script (before # AC_OUTPUT is called), incase it is used in configure for compilation # tests. AC_DEFUN([LT_OUTPUT], [: ${CONFIG_LT=./config.lt} AC_MSG_NOTICE([creating $CONFIG_LT]) _LT_GENERATED_FILE_INIT(["$CONFIG_LT"], [# Run this file to recreate a libtool stub with the current configuration.]) cat >>"$CONFIG_LT" <<\_LTEOF lt_cl_silent=false exec AS_MESSAGE_LOG_FD>>config.log { echo AS_BOX([Running $as_me.]) } >&AS_MESSAGE_LOG_FD lt_cl_help="\ '$as_me' creates a local libtool stub from the current configuration, for use in further configure time tests before the real libtool is generated. Usage: $[0] [[OPTIONS]] -h, --help print this help, then exit -V, --version print version number, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files Report bugs to ." lt_cl_version="\ m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) configured by $[0], generated by m4_PACKAGE_STRING. Copyright (C) 2011 Free Software Foundation, Inc. This config.lt script is free software; the Free Software Foundation gives unlimited permision to copy, distribute and modify it." while test 0 != $[#] do case $[1] in --version | --v* | -V ) echo "$lt_cl_version"; exit 0 ;; --help | --h* | -h ) echo "$lt_cl_help"; exit 0 ;; --debug | --d* | -d ) debug=: ;; --quiet | --q* | --silent | --s* | -q ) lt_cl_silent=: ;; -*) AC_MSG_ERROR([unrecognized option: $[1] Try '$[0] --help' for more information.]) ;; *) AC_MSG_ERROR([unrecognized argument: $[1] Try '$[0] --help' for more information.]) ;; esac shift done if $lt_cl_silent; then exec AS_MESSAGE_FD>/dev/null fi _LTEOF cat >>"$CONFIG_LT" <<_LTEOF _LT_OUTPUT_LIBTOOL_COMMANDS_INIT _LTEOF cat >>"$CONFIG_LT" <<\_LTEOF AC_MSG_NOTICE([creating $ofile]) _LT_OUTPUT_LIBTOOL_COMMANDS AS_EXIT(0) _LTEOF chmod +x "$CONFIG_LT" # configure is writing to config.log, but config.lt does its own redirection, # appending to config.log, which fails on DOS, as config.log is still kept # open by configure. Here we exec the FD to /dev/null, effectively closing # config.log, so it can be properly (re)opened and appended to by config.lt. lt_cl_success=: test yes = "$silent" && lt_config_lt_args="$lt_config_lt_args --quiet" exec AS_MESSAGE_LOG_FD>/dev/null $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false exec AS_MESSAGE_LOG_FD>>config.log $lt_cl_success || AS_EXIT(1) ])# LT_OUTPUT # _LT_CONFIG(TAG) # --------------- # If TAG is the built-in tag, create an initial libtool script with a # default configuration from the untagged config vars. Otherwise add code # to config.status for appending the configuration named by TAG from the # matching tagged config vars. m4_defun([_LT_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_CONFIG_SAVE_COMMANDS([ m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl m4_if(_LT_TAG, [C], [ # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi cfgfile=${ofile}T trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # Generated automatically by $as_me ($PACKAGE) $VERSION # Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: # NOTE: Changes made to this file will be lost: look at ltmain.sh. # Provide generalized library-building support services. # Written by Gordon Matzigkeit, 1996 _LT_COPYING _LT_LIBTOOL_TAGS # Configured defaults for sys_lib_dlsearch_path munging. : \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} # ### BEGIN LIBTOOL CONFIG _LT_LIBTOOL_CONFIG_VARS _LT_LIBTOOL_TAG_VARS # ### END LIBTOOL CONFIG _LT_EOF cat <<'_LT_EOF' >> "$cfgfile" # ### BEGIN FUNCTIONS SHARED WITH CONFIGURE _LT_PREPARE_MUNGE_PATH_LIST _LT_PREPARE_CC_BASENAME # ### END FUNCTIONS SHARED WITH CONFIGURE _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac _LT_PROG_LTMAIN # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? sed '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" ], [cat <<_LT_EOF >> "$ofile" dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded dnl in a comment (ie after a #). # ### BEGIN LIBTOOL TAG CONFIG: $1 _LT_LIBTOOL_TAG_VARS(_LT_TAG) # ### END LIBTOOL TAG CONFIG: $1 _LT_EOF ])dnl /m4_if ], [m4_if([$1], [], [ PACKAGE='$PACKAGE' VERSION='$VERSION' RM='$RM' ofile='$ofile'], []) ])dnl /_LT_CONFIG_SAVE_COMMANDS ])# _LT_CONFIG # LT_SUPPORTED_TAG(TAG) # --------------------- # Trace this macro to discover what tags are supported by the libtool # --tag option, using: # autoconf --trace 'LT_SUPPORTED_TAG:$1' AC_DEFUN([LT_SUPPORTED_TAG], []) # C support is built-in for now m4_define([_LT_LANG_C_enabled], []) m4_define([_LT_TAGS], []) # LT_LANG(LANG) # ------------- # Enable libtool support for the given language if not already enabled. AC_DEFUN([LT_LANG], [AC_BEFORE([$0], [LT_OUTPUT])dnl m4_case([$1], [C], [_LT_LANG(C)], [C++], [_LT_LANG(CXX)], [Go], [_LT_LANG(GO)], [Java], [_LT_LANG(GCJ)], [Fortran 77], [_LT_LANG(F77)], [Fortran], [_LT_LANG(FC)], [Windows Resource], [_LT_LANG(RC)], [m4_ifdef([_LT_LANG_]$1[_CONFIG], [_LT_LANG($1)], [m4_fatal([$0: unsupported language: "$1"])])])dnl ])# LT_LANG # _LT_LANG(LANGNAME) # ------------------ m4_defun([_LT_LANG], [m4_ifdef([_LT_LANG_]$1[_enabled], [], [LT_SUPPORTED_TAG([$1])dnl m4_append([_LT_TAGS], [$1 ])dnl m4_define([_LT_LANG_]$1[_enabled], [])dnl _LT_LANG_$1_CONFIG($1)])dnl ])# _LT_LANG m4_ifndef([AC_PROG_GO], [ ############################################################ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_GO. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # ############################################################ m4_defun([AC_PROG_GO], [AC_LANG_PUSH(Go)dnl AC_ARG_VAR([GOC], [Go compiler command])dnl AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl _AC_ARG_VAR_LDFLAGS()dnl AC_CHECK_TOOL(GOC, gccgo) if test -z "$GOC"; then if test -n "$ac_tool_prefix"; then AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo]) fi fi if test -z "$GOC"; then AC_CHECK_PROG(GOC, gccgo, gccgo, false) fi ])#m4_defun ])#m4_ifndef # _LT_LANG_DEFAULT_CONFIG # ----------------------- m4_defun([_LT_LANG_DEFAULT_CONFIG], [AC_PROVIDE_IFELSE([AC_PROG_CXX], [LT_LANG(CXX)], [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) AC_PROVIDE_IFELSE([AC_PROG_F77], [LT_LANG(F77)], [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) AC_PROVIDE_IFELSE([AC_PROG_FC], [LT_LANG(FC)], [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal dnl pulling things in needlessly. AC_PROVIDE_IFELSE([AC_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([LT_PROG_GCJ], [LT_LANG(GCJ)], [m4_ifdef([AC_PROG_GCJ], [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([A][M_PROG_GCJ], [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([LT_PROG_GCJ], [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) AC_PROVIDE_IFELSE([AC_PROG_GO], [LT_LANG(GO)], [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])]) AC_PROVIDE_IFELSE([LT_PROG_RC], [LT_LANG(RC)], [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) ])# _LT_LANG_DEFAULT_CONFIG # Obsolete macros: AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_CXX], []) dnl AC_DEFUN([AC_LIBTOOL_F77], []) dnl AC_DEFUN([AC_LIBTOOL_FC], []) dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) dnl AC_DEFUN([AC_LIBTOOL_RC], []) # _LT_TAG_COMPILER # ---------------- m4_defun([_LT_TAG_COMPILER], [AC_REQUIRE([AC_PROG_CC])dnl _LT_DECL([LTCC], [CC], [1], [A C compiler])dnl _LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl _LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl _LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC ])# _LT_TAG_COMPILER # _LT_COMPILER_BOILERPLATE # ------------------------ # Check for compiler boilerplate output or warnings with # the simple compiler test code. m4_defun([_LT_COMPILER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ])# _LT_COMPILER_BOILERPLATE # _LT_LINKER_BOILERPLATE # ---------------------- # Check for linker boilerplate output or warnings with # the simple link test code. m4_defun([_LT_LINKER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ])# _LT_LINKER_BOILERPLATE # _LT_REQUIRED_DARWIN_CHECKS # ------------------------- m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ case $host_os in rhapsody* | darwin*) AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) AC_CHECK_TOOL([LIPO], [lipo], [:]) AC_CHECK_TOOL([OTOOL], [otool], [:]) AC_CHECK_TOOL([OTOOL64], [otool64], [:]) _LT_DECL([], [DSYMUTIL], [1], [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) _LT_DECL([], [NMEDIT], [1], [Tool to change global to local symbols on Mac OS X]) _LT_DECL([], [LIPO], [1], [Tool to manipulate fat objects and archives on Mac OS X]) _LT_DECL([], [OTOOL], [1], [ldd/readelf like tool for Mach-O binaries on Mac OS X]) _LT_DECL([], [OTOOL64], [1], [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], [lt_cv_apple_cc_single_mod=no if test -z "$LT_MULTI_MODULE"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? # If there is a non-empty error log, and "single_module" # appears in it, assume the flag caused a linker warning if test -s conftest.err && $GREP single_module conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD # Otherwise, if the output was created with a 0 exit code from # the compiler, it worked. elif test -f libconftest.dylib && test 0 = "$_lt_result"; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -rf libconftest.dylib* rm -f conftest.* fi]) AC_CACHE_CHECK([for -exported_symbols_list linker flag], [lt_cv_ld_exported_symbols_list], [lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [lt_cv_ld_exported_symbols_list=yes], [lt_cv_ld_exported_symbols_list=no]) LDFLAGS=$save_LDFLAGS ]) AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load], [lt_cv_ld_force_load=no cat > conftest.c << _LT_EOF int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD echo "$AR $AR_FLAGS libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD $AR $AR_FLAGS libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD cat > conftest.c << _LT_EOF int main() { return 0;} _LT_EOF echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err _lt_result=$? if test -s conftest.err && $GREP force_load conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then lt_cv_ld_force_load=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -f conftest.err libconftest.a conftest conftest.c rm -rf conftest.dSYM ]) case $host_os in rhapsody* | darwin1.[[012]]) _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; darwin*) # darwin 5.x on # if running on 10.5 or later, the deployment target defaults # to the OS version, if on x86, and 10.4, the deployment # target defaults to 10.4. Don't you love it? case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; 10.[[012]][[,.]]*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; 10.*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test yes = "$lt_cv_apple_cc_single_mod"; then _lt_dar_single_mod='$single_module' fi if test yes = "$lt_cv_ld_exported_symbols_list"; then _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' fi if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac ]) # _LT_DARWIN_LINKER_FEATURES([TAG]) # --------------------------------- # Checks for linker and compiler features on darwin m4_defun([_LT_DARWIN_LINKER_FEATURES], [ m4_require([_LT_REQUIRED_DARWIN_CHECKS]) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported if test yes = "$lt_cv_ld_force_load"; then _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes], [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes]) else _LT_TAGVAR(whole_archive_flag_spec, $1)='' fi _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=$_lt_dar_allow_undefined case $cc_basename in ifort*|nagfor*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test yes = "$_lt_dar_can_shared"; then output_verbose_link_cmd=func_echo_all _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" m4_if([$1], [CXX], [ if test yes != "$lt_cv_apple_cc_single_mod"; then _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" fi ],[]) else _LT_TAGVAR(ld_shlibs, $1)=no fi ]) # _LT_SYS_MODULE_PATH_AIX([TAGNAME]) # ---------------------------------- # Links a minimal program and checks the executable # for the system default hardcoded library path. In most cases, # this is /usr/lib:/lib, but when the MPI compilers are used # the location of the communication and MPI libs are included too. # If we don't find anything, use the default library path according # to the aix ld manual. # Store the results from the different compilers for each TAGNAME. # Allow to override them for all tags through lt_cv_aix_libpath. m4_defun([_LT_SYS_MODULE_PATH_AIX], [m4_require([_LT_DECL_SED])dnl if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])], [AC_LINK_IFELSE([AC_LANG_PROGRAM],[ lt_aix_libpath_sed='[ /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }]' _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi],[]) if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=/usr/lib:/lib fi ]) aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1]) fi ])# _LT_SYS_MODULE_PATH_AIX # _LT_SHELL_INIT(ARG) # ------------------- m4_define([_LT_SHELL_INIT], [m4_divert_text([M4SH-INIT], [$1 ])])# _LT_SHELL_INIT # _LT_PROG_ECHO_BACKSLASH # ----------------------- # Find how we can fake an echo command that does not interpret backslash. # In particular, with Autoconf 2.60 or later we add some code to the start # of the generated configure script that will find a shell with a builtin # printf (that we can use as an echo command). m4_defun([_LT_PROG_ECHO_BACKSLASH], [ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO AC_MSG_CHECKING([how to print strings]) # Test print first, because it will be a builtin if present. if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='print -r --' elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='printf %s\n' else # Use this function as a fallback that always works. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $[]1 _LTECHO_EOF' } ECHO='func_fallback_echo' fi # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } case $ECHO in printf*) AC_MSG_RESULT([printf]) ;; print*) AC_MSG_RESULT([print -r]) ;; *) AC_MSG_RESULT([cat]) ;; esac m4_ifdef([_AS_DETECT_SUGGESTED], [_AS_DETECT_SUGGESTED([ test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || ( ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO PATH=/empty FPATH=/empty; export PATH FPATH test "X`printf %s $ECHO`" = "X$ECHO" \ || test "X`print -r -- $ECHO`" = "X$ECHO" )])]) _LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) _LT_DECL([], [ECHO], [1], [An echo program that protects backslashes]) ])# _LT_PROG_ECHO_BACKSLASH # _LT_WITH_SYSROOT # ---------------- AC_DEFUN([_LT_WITH_SYSROOT], [AC_MSG_CHECKING([for sysroot]) AC_ARG_WITH([sysroot], [AS_HELP_STRING([--with-sysroot@<:@=DIR@:>@], [Search for dependent libraries within DIR (or the compiler's sysroot if not specified).])], [], [with_sysroot=no]) dnl lt_sysroot will always be passed unquoted. We quote it here dnl in case the user passed a directory name. lt_sysroot= case $with_sysroot in #( yes) if test yes = "$GCC"; then lt_sysroot=`$CC --print-sysroot 2>/dev/null` fi ;; #( /*) lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` ;; #( no|'') ;; #( *) AC_MSG_RESULT([$with_sysroot]) AC_MSG_ERROR([The sysroot must be an absolute path.]) ;; esac AC_MSG_RESULT([${lt_sysroot:-no}]) _LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl [dependent libraries, and where our libraries should be installed.])]) # _LT_ENABLE_LOCK # --------------- m4_defun([_LT_ENABLE_LOCK], [AC_ARG_ENABLE([libtool-lock], [AS_HELP_STRING([--disable-libtool-lock], [avoid locking (might break parallel builds)])]) test no = "$enable_libtool_lock" || enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out what ABI is being produced by ac_compile, and set mode # options accordingly. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE=32 ;; *ELF-64*) HPUX_IA64_MODE=64 ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then if test yes = "$lt_cv_prog_gnu_ld"; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; mips64*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then emul=elf case `/usr/bin/file conftest.$ac_objext` in *32-bit*) emul="${emul}32" ;; *64-bit*) emul="${emul}64" ;; esac case `/usr/bin/file conftest.$ac_objext` in *MSB*) emul="${emul}btsmip" ;; *LSB*) emul="${emul}ltsmip" ;; esac case `/usr/bin/file conftest.$ac_objext` in *N32*) emul="${emul}n32" ;; esac LD="${LD-ld} -m $emul" fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. Note that the listed cases only cover the # situations where additional linker options are needed (such as when # doing 32-bit compilation for a host where ld defaults to 64-bit, or # vice versa); the common cases where no linker options are needed do # not appear in the list. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) case `/usr/bin/file conftest.o` in *x86-64*) LD="${LD-ld} -m elf32_x86_64" ;; *) LD="${LD-ld} -m elf_i386" ;; esac ;; powerpc64le-*linux*) LD="${LD-ld} -m elf32lppclinux" ;; powerpc64-*linux*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; powerpcle-*linux*) LD="${LD-ld} -m elf64lppc" ;; powerpc-*linux*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -belf" AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, [AC_LANG_PUSH(C) AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) AC_LANG_POP]) if test yes != "$lt_cv_cc_needs_belf"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS=$SAVE_CFLAGS fi ;; *-*solaris*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) case $host in i?86-*-solaris*|x86_64-*-solaris*) LD="${LD-ld} -m elf_x86_64" ;; sparc*-*-solaris*) LD="${LD-ld} -m elf64_sparc" ;; esac # GNU ld 2.21 introduced _sol2 emulations. Use them if available. if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then LD=${LD-ld}_sol2 fi ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks=$enable_libtool_lock ])# _LT_ENABLE_LOCK # _LT_PROG_AR # ----------- m4_defun([_LT_PROG_AR], [AC_CHECK_TOOLS(AR, [ar], false) : ${AR=ar} _LT_DECL([], [AR], [1], [The archiver]) # Use ARFLAGS variable as AR's operation code to sync the variable naming with # Automake. If both AR_FLAGS and ARFLAGS are specified, AR_FLAGS should have # higher priority because thats what people were doing historically (setting # ARFLAGS for automake and AR_FLAGS for libtool). FIXME: Make the AR_FLAGS # variable obsoleted/removed. test ${AR_FLAGS+y} || AR_FLAGS=${ARFLAGS-cr} lt_ar_flags=$AR_FLAGS _LT_DECL([], [lt_ar_flags], [0], [Flags to create an archive (by configure)]) # Make AR_FLAGS overridable by 'make ARFLAGS='. Don't try to run-time override # by AR_FLAGS because that was never working and AR_FLAGS is about to die. _LT_DECL([], [AR_FLAGS], [\@S|@{ARFLAGS-"\@S|@lt_ar_flags"}], [Flags to create an archive]) AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], [lt_cv_ar_at_file=no AC_COMPILE_IFELSE([AC_LANG_PROGRAM], [echo conftest.$ac_objext > conftest.lst lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD' AC_TRY_EVAL([lt_ar_try]) if test 0 -eq "$ac_status"; then # Ensure the archiver fails upon bogus file names. rm -f conftest.$ac_objext libconftest.a AC_TRY_EVAL([lt_ar_try]) if test 0 -ne "$ac_status"; then lt_cv_ar_at_file=@ fi fi rm -f conftest.* libconftest.a ]) ]) if test no = "$lt_cv_ar_at_file"; then archiver_list_spec= else archiver_list_spec=$lt_cv_ar_at_file fi _LT_DECL([], [archiver_list_spec], [1], [How to feed a file listing to the archiver]) ])# _LT_PROG_AR # _LT_CMD_OLD_ARCHIVE # ------------------- m4_defun([_LT_CMD_OLD_ARCHIVE], [_LT_PROG_AR AC_CHECK_TOOL(STRIP, strip, :) test -z "$STRIP" && STRIP=: _LT_DECL([], [STRIP], [1], [A symbol stripping program]) AC_CHECK_TOOL(RANLIB, ranlib, :) test -z "$RANLIB" && RANLIB=: _LT_DECL([], [RANLIB], [1], [Commands used to install an old-style archive]) # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in bitrig* | openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" fi case $host_os in darwin*) lock_old_archive_extraction=yes ;; *) lock_old_archive_extraction=no ;; esac _LT_DECL([], [old_postinstall_cmds], [2]) _LT_DECL([], [old_postuninstall_cmds], [2]) _LT_TAGDECL([], [old_archive_cmds], [2], [Commands used to build an old-style archive]) _LT_DECL([], [lock_old_archive_extraction], [0], [Whether to use a lock for old archive extraction]) ])# _LT_CMD_OLD_ARCHIVE # _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------------------- # Check whether the given compiler option works AC_DEFUN([_LT_COMPILER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$3" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi fi $RM conftest* ]) if test yes = "[$]$2"; then m4_if([$5], , :, [$5]) else m4_if([$6], , :, [$6]) fi ])# _LT_COMPILER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) # _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------- # Check whether the given linker option works AC_DEFUN([_LT_LINKER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $3" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&AS_MESSAGE_LOG_FD $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi else $2=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS ]) if test yes = "[$]$2"; then m4_if([$4], , :, [$4]) else m4_if([$5], , :, [$5]) fi ])# _LT_LINKER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) # LT_CMD_MAX_LEN #--------------- AC_DEFUN([LT_CMD_MAX_LEN], [AC_REQUIRE([AC_CANONICAL_HOST])dnl # find the maximum length of command line arguments AC_MSG_CHECKING([the maximum length of command line arguments]) AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl i=0 teststring=ABCD case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; mint*) # On MiNT this can take a long time and run out of memory. lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; os2*) # The test takes a long time on OS/2. lt_cv_sys_max_cmd_len=8192 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len" && \ test undefined != "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test X`env echo "$teststring$teststring" 2>/dev/null` \ = "X$teststring$teststring"; } >/dev/null 2>&1 && test 17 != "$i" # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac ]) if test -n "$lt_cv_sys_max_cmd_len"; then AC_MSG_RESULT($lt_cv_sys_max_cmd_len) else AC_MSG_RESULT(none) fi max_cmd_len=$lt_cv_sys_max_cmd_len _LT_DECL([], [max_cmd_len], [0], [What is the maximum length of a command?]) ])# LT_CMD_MAX_LEN # Old name: AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) # _LT_HEADER_DLFCN # ---------------- m4_defun([_LT_HEADER_DLFCN], [AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl ])# _LT_HEADER_DLFCN # _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, # ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) # ---------------------------------------------------------------- m4_defun([_LT_TRY_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test yes = "$cross_compiling"; then : [$4] else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF [#line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; }] _LT_EOF if AC_TRY_EVAL(ac_link) && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) $1 ;; x$lt_dlneed_uscore) $2 ;; x$lt_dlunknown|x*) $3 ;; esac else : # compilation failed $3 fi fi rm -fr conftest* ])# _LT_TRY_DLOPEN_SELF # LT_SYS_DLOPEN_SELF # ------------------ AC_DEFUN([LT_SYS_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test yes != "$enable_dlopen"; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen=load_add_on lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen=LoadLibrary lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen=dlopen lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],[ lt_cv_dlopen=dyld lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ]) ;; tpf*) # Don't try to run any link tests for TPF. We know it's impossible # because TPF is a cross-compiler, and we know how we open DSOs. lt_cv_dlopen=dlopen lt_cv_dlopen_libs= lt_cv_dlopen_self=no ;; *) AC_CHECK_FUNC([shl_load], [lt_cv_dlopen=shl_load], [AC_CHECK_LIB([dld], [shl_load], [lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld], [AC_CHECK_FUNC([dlopen], [lt_cv_dlopen=dlopen], [AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl], [AC_CHECK_LIB([svld], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld], [AC_CHECK_LIB([dld], [dld_link], [lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld]) ]) ]) ]) ]) ]) ;; esac if test no = "$lt_cv_dlopen"; then enable_dlopen=no else enable_dlopen=yes fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS=$CPPFLAGS test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS=$LDFLAGS wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS=$LIBS LIBS="$lt_cv_dlopen_libs $LIBS" AC_CACHE_CHECK([whether a program can dlopen itself], lt_cv_dlopen_self, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) ]) if test yes = "$lt_cv_dlopen_self"; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" AC_CACHE_CHECK([whether a statically linked program can dlopen itself], lt_cv_dlopen_self_static, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) ]) fi CPPFLAGS=$save_CPPFLAGS LDFLAGS=$save_LDFLAGS LIBS=$save_LIBS ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi _LT_DECL([dlopen_support], [enable_dlopen], [0], [Whether dlopen is supported]) _LT_DECL([dlopen_self], [enable_dlopen_self], [0], [Whether dlopen of programs is supported]) _LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], [Whether dlopen of statically linked programs is supported]) ])# LT_SYS_DLOPEN_SELF # Old name: AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) # _LT_COMPILER_C_O([TAGNAME]) # --------------------------- # Check to see if options -c and -o are simultaneously supported by compiler. # This macro does not hard code the compiler like AC_PROG_CC_C_O. m4_defun([_LT_COMPILER_C_O], [m4_require([_LT_DECL_SED])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes fi fi chmod u+w . 2>&AS_MESSAGE_LOG_FD $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* ]) _LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], [Does compiler simultaneously support -c and -o options?]) ])# _LT_COMPILER_C_O # _LT_COMPILER_FILE_LOCKS([TAGNAME]) # ---------------------------------- # Check to see if we can do hard links to lock some files if needed m4_defun([_LT_COMPILER_FILE_LOCKS], [m4_require([_LT_ENABLE_LOCK])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_COMPILER_C_O([$1]) hard_links=nottested if test no = "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" && test no != "$need_locks"; then # do not overwrite the value of need_locks provided by the user AC_MSG_CHECKING([if we can lock with hard links]) hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no AC_MSG_RESULT([$hard_links]) if test no = "$hard_links"; then AC_MSG_WARN(['$CC' does not support '-c -o', so 'make -j' may be unsafe]) need_locks=warn fi else need_locks=no fi _LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) ])# _LT_COMPILER_FILE_LOCKS # _LT_CHECK_OBJDIR # ---------------- m4_defun([_LT_CHECK_OBJDIR], [AC_CACHE_CHECK([for objdir], [lt_cv_objdir], [rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null]) objdir=$lt_cv_objdir _LT_DECL([], [objdir], [0], [The name of the directory that contains temporary libtool files])dnl m4_pattern_allow([LT_OBJDIR])dnl AC_DEFINE_UNQUOTED([LT_OBJDIR], "$lt_cv_objdir/", [Define to the sub-directory where libtool stores uninstalled libraries.]) ])# _LT_CHECK_OBJDIR # _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) # -------------------------------------- # Check hardcoding attributes. m4_defun([_LT_LINKER_HARDCODE_LIBPATH], [AC_MSG_CHECKING([how to hardcode library paths into programs]) _LT_TAGVAR(hardcode_action, $1)= if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || test -n "$_LT_TAGVAR(runpath_var, $1)" || test yes = "$_LT_TAGVAR(hardcode_automatic, $1)"; then # We can hardcode non-existent directories. if test no != "$_LT_TAGVAR(hardcode_direct, $1)" && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" && test no != "$_LT_TAGVAR(hardcode_minus_L, $1)"; then # Linking always hardcodes the temporary library directory. _LT_TAGVAR(hardcode_action, $1)=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. _LT_TAGVAR(hardcode_action, $1)=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. _LT_TAGVAR(hardcode_action, $1)=unsupported fi AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) if test relink = "$_LT_TAGVAR(hardcode_action, $1)" || test yes = "$_LT_TAGVAR(inherit_rpath, $1)"; then # Fast installation is not supported enable_fast_install=no elif test yes = "$shlibpath_overrides_runpath" || test no = "$enable_shared"; then # Fast installation is not necessary enable_fast_install=needless fi _LT_TAGDECL([], [hardcode_action], [0], [How to hardcode a shared library path into an executable]) ])# _LT_LINKER_HARDCODE_LIBPATH # _LT_CMD_STRIPLIB # ---------------- m4_defun([_LT_CMD_STRIPLIB], [m4_require([_LT_DECL_EGREP]) striplib= old_striplib= AC_MSG_CHECKING([whether stripping libraries is possible]) if test -z "$STRIP"; then AC_MSG_RESULT([no]) else if $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then old_striplib="$STRIP --strip-debug" striplib="$STRIP --strip-unneeded" AC_MSG_RESULT([yes]) else case $host_os in darwin*) # FIXME - insert some real tests, host_os isn't really good enough striplib="$STRIP -x" old_striplib="$STRIP -S" AC_MSG_RESULT([yes]) ;; freebsd*) if $STRIP -V 2>&1 | $GREP "elftoolchain" >/dev/null; then old_striplib="$STRIP --strip-debug" striplib="$STRIP --strip-unneeded" AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi ;; *) AC_MSG_RESULT([no]) ;; esac fi fi _LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) _LT_DECL([], [striplib], [1]) ])# _LT_CMD_STRIPLIB # _LT_PREPARE_MUNGE_PATH_LIST # --------------------------- # Make sure func_munge_path_list() is defined correctly. m4_defun([_LT_PREPARE_MUNGE_PATH_LIST], [[# func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x@S|@2 in x) ;; *:) eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'` \@S|@@S|@1\" ;; x:*) eval @S|@1=\"\@S|@@S|@1 `$ECHO @S|@2 | $SED 's/:/ /g'`\" ;; *::*) eval @S|@1=\"\@S|@@S|@1\ `$ECHO @S|@2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval @S|@1=\"`$ECHO @S|@2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \@S|@@S|@1\" ;; *) eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'`\" ;; esac } ]])# _LT_PREPARE_PATH_LIST # _LT_SYS_DYNAMIC_LINKER([TAG]) # ----------------------------- # PORTME Fill in your ld.so characteristics m4_defun([_LT_SYS_DYNAMIC_LINKER], [AC_REQUIRE([AC_CANONICAL_HOST])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_OBJDUMP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl m4_require([_LT_PREPARE_MUNGE_PATH_LIST])dnl AC_MSG_CHECKING([dynamic linker characteristics]) m4_if([$1], [], [ if test yes = "$GCC"; then case $host_os in darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; *) lt_awk_arg='/^libraries:/' ;; esac case $host_os in mingw* | cegcc*) lt_sed_strip_eq='s|=\([[A-Za-z]]:\)|\1|g' ;; *) lt_sed_strip_eq='s|=/|/|g' ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` case $lt_search_path_spec in *\;*) # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` ;; *) lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` ;; esac # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary... lt_tmp_lt_search_path_spec= lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` # ...but if some path component already ends with the multilib dir we assume # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). case "$lt_multi_os_dir; $lt_search_path_spec " in "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) lt_multi_os_dir= ;; esac for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" elif test -n "$lt_multi_os_dir"; then test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' BEGIN {RS = " "; FS = "/|\n";} { lt_foo = ""; lt_count = 0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo = "/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[[lt_foo]]++; } if (lt_freq[[lt_foo]] == 1) { print lt_foo; } }'` # AWK program above erroneously prepends '/' to C:/dos/paths # for these hosts. case $host_os in mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ $SED 's|/\([[A-Za-z]]:\)|\1|g'` ;; esac sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi]) library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=.so postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown AC_ARG_VAR([LT_SYS_LIBRARY_PATH], [User-defined run-time library search path.]) case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='$libname$release$shared_ext$major' ;; aix[[4-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test ia64 = "$host_cpu"; then # AIX 5 supports IA64 library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line '#! .'. This would cause the generated library to # depend on '.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[[01]] | aix4.[[01]].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # Using Import Files as archive members, it is possible to support # filename-based versioning of shared library archives on AIX. While # this would work for both with and without runtime linking, it will # prevent static linking of such archives. So we do filename-based # shared library versioning with .so extension only, which is used # when both runtime linking and shared linking is enabled. # Unfortunately, runtime linking may impact performance, so we do # not want this to be the default eventually. Also, we use the # versioned .so libs for executables only if there is the -brtl # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. # To allow for filename-based versioning support, we need to create # libNAME.so.V as an archive file, containing: # *) an Import File, referring to the versioned filename of the # archive as well as the shared archive member, telling the # bitwidth (32 or 64) of that shared object, and providing the # list of exported symbols of that shared object, eventually # decorated with the 'weak' keyword # *) the shared object with the F_LOADONLY flag set, to really avoid # it being seen by the linker. # At run time we better use the real file rather than another symlink, # but for link time we create the symlink libNAME.so -> libNAME.so.V case $with_aix_soname,$aix_use_runtimelinking in # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. aix,yes) # traditional libtool dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; aix,no) # traditional AIX only dynamic_linker='AIX lib.a[(]lib.so.V[)]' # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' ;; svr4,*) # full svr4 only dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)]" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,yes) # both, prefer svr4 dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)], lib.a[(]lib.so.V[)]" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # unpreferred sharedlib libNAME.a needs extra handling postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,no) # both, prefer aix dynamic_linker="AIX lib.a[(]lib.so.V[)], lib.so.V[(]$shared_archive_member_spec.o[)]" library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' ;; esac shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='$libname$shared_ext' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[[45]]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl* | *,icl*) # Native MSVC or ICC libname_spec='$name' soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' library_names_spec='$libname.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec=$LIB if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC and ICC wrapper library_names_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' soname_spec='$libname$release$major$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[[23]].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[[01]]* | freebsdelf3.[[01]]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=no sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' if test 32 = "$HPUX_IA64_MODE"; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[[3-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test yes = "$lt_cv_prog_gnu_ld"; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; linux*android*) version_type=none # Android doesn't support versioned libraries. need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext' soname_spec='$libname$release$shared_ext' finish_cmds= shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes dynamic_linker='Android linker' # Don't embed -rpath directories since the linker doesn't support them. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath], [lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], [lt_cv_shlibpath_overrides_runpath=yes])]) LDFLAGS=$save_LDFLAGS libdir=$save_libdir ]) shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, # even though it is searched at run-time. Try to do the best guess by # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd* | bitrig*) version_type=sunos sys_lib_dlsearch_path_spec=/usr/lib need_lib_prefix=no if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then need_version=no else need_version=yes fi library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; os2*) libname_spec='$name' version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no # OS/2 can only load a DLL with a base name of 8 characters or less. soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; v=$($ECHO $release$versuffix | tr -d .-); n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); $ECHO $n$v`$shared_ext' library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' shlibpath_var=BEGINLIBPATH sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test yes = "$with_gnu_ld"; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec; then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' soname_spec='$libname$shared_ext.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=sco need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test yes = "$with_gnu_ld"; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac AC_MSG_RESULT([$dynamic_linker]) test no = "$dynamic_linker" && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test yes = "$GCC"; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec fi if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi # remember unaugmented sys_lib_dlsearch_path content for libtool script decls... configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec # ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" # to be used as default LT_SYS_LIBRARY_PATH value in generated libtool configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH _LT_DECL([], [variables_saved_for_relink], [1], [Variables whose values should be saved in libtool wrapper scripts and restored at link time]) _LT_DECL([], [need_lib_prefix], [0], [Do we need the "lib" prefix for modules?]) _LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) _LT_DECL([], [version_type], [0], [Library versioning type]) _LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) _LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) _LT_DECL([], [shlibpath_overrides_runpath], [0], [Is shlibpath searched before the hard-coded library search path?]) _LT_DECL([], [libname_spec], [1], [Format of library name prefix]) _LT_DECL([], [library_names_spec], [1], [[List of archive names. First name is the real one, the rest are links. The last name is the one that the linker finds with -lNAME]]) _LT_DECL([], [soname_spec], [1], [[The coded name of the library, if different from the real name]]) _LT_DECL([], [install_override_mode], [1], [Permission mode override for installation of shared libraries]) _LT_DECL([], [postinstall_cmds], [2], [Command to use after installation of a shared archive]) _LT_DECL([], [postuninstall_cmds], [2], [Command to use after uninstallation of a shared archive]) _LT_DECL([], [finish_cmds], [2], [Commands used to finish a libtool library installation in a directory]) _LT_DECL([], [finish_eval], [1], [[As "finish_cmds", except a single script fragment to be evaled but not shown]]) _LT_DECL([], [hardcode_into_libs], [0], [Whether we should hardcode library paths into libraries]) _LT_DECL([], [sys_lib_search_path_spec], [2], [Compile-time system search path for libraries]) _LT_DECL([sys_lib_dlsearch_path_spec], [configure_time_dlsearch_path], [2], [Detected run-time system search path for libraries]) _LT_DECL([], [configure_time_lt_sys_library_path], [2], [Explicit LT_SYS_LIBRARY_PATH set during ./configure time]) ])# _LT_SYS_DYNAMIC_LINKER # _LT_PATH_TOOL_PREFIX(TOOL) # -------------------------- # find a file program that can recognize shared library AC_DEFUN([_LT_PATH_TOOL_PREFIX], [m4_require([_LT_DECL_EGREP])dnl AC_MSG_CHECKING([for $1]) AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, [case $MAGIC_CMD in [[\\/*] | ?:[\\/]*]) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR dnl $ac_dummy forces splitting on constant user-supplied paths. dnl POSIX.2 word splitting is done only on the output of word expansions, dnl not every word. This closes a longstanding sh security hole. ac_dummy="m4_if([$2], , $PATH, [$2])" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$1"; then lt_cv_path_MAGIC_CMD=$ac_dir/"$1" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac]) MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then AC_MSG_RESULT($MAGIC_CMD) else AC_MSG_RESULT(no) fi _LT_DECL([], [MAGIC_CMD], [0], [Used to examine libraries when file_magic_cmd begins with "file"])dnl ])# _LT_PATH_TOOL_PREFIX # Old name: AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) # _LT_PATH_MAGIC # -------------- # find a file program that can recognize a shared library m4_defun([_LT_PATH_MAGIC], [_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) else MAGIC_CMD=: fi fi ])# _LT_PATH_MAGIC # LT_PATH_LD # ---------- # find the pathname to the GNU or non-GNU linker AC_DEFUN([LT_PATH_LD], [AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PROG_ECHO_BACKSLASH])dnl AC_ARG_WITH([gnu-ld], [AS_HELP_STRING([--with-gnu-ld], [assume the C compiler uses GNU ld @<:@default=no@:>@])], [test no = "$withval" || with_gnu_ld=yes], [with_gnu_ld=no])dnl ac_prog=ld if test yes = "$GCC"; then # Check if gcc -print-prog-name=ld gives a path. AC_MSG_CHECKING([for ld used by $CC]) case $host in *-*-mingw*) # gcc leaves a trailing carriage return, which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [[\\/]]* | ?:[[\\/]]*) re_direlt='/[[^/]][[^/]]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD=$ac_prog ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test yes = "$with_gnu_ld"; then AC_MSG_CHECKING([for GNU ld]) else AC_MSG_CHECKING([for non-GNU ld]) fi AC_CACHE_VAL(lt_cv_path_LD, [if test -z "$LD"; then lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD=$ac_dir/$ac_prog # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &1 conftest.i cat conftest.i conftest.i >conftest2.i : ${lt_DD:=$DD} AC_PATH_PROGS_FEATURE_CHECK([lt_DD], [dd], [if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: fi]) rm -f conftest.i conftest2.i conftest.out]) ])# _LT_PATH_DD # _LT_CMD_TRUNCATE # ---------------- # find command to truncate a binary pipe m4_defun([_LT_CMD_TRUNCATE], [m4_require([_LT_PATH_DD]) AC_CACHE_CHECK([how to truncate binary pipes], [lt_cv_truncate_bin], [printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i lt_cv_truncate_bin= if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" fi rm -f conftest.i conftest2.i conftest.out test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"]) _LT_DECL([lt_truncate_bin], [lt_cv_truncate_bin], [1], [Command to truncate a binary pipe]) ])# _LT_CMD_TRUNCATE # _LT_CHECK_MAGIC_METHOD # ---------------------- # how to check for library dependencies # -- PORTME fill in with the dynamic library characteristics m4_defun([_LT_CHECK_MAGIC_METHOD], [m4_require([_LT_DECL_EGREP]) m4_require([_LT_DECL_OBJDUMP]) AC_CACHE_CHECK([how to recognize dependent libraries], lt_cv_deplibs_check_method, [lt_cv_file_magic_cmd='$MAGIC_CMD' lt_cv_file_magic_test_file= lt_cv_deplibs_check_method='unknown' # Need to set the preceding variable on all platforms that support # interlibrary dependencies. # 'none' -- dependencies not supported. # 'unknown' -- same as none, but documents that we really don't know. # 'pass_all' -- all dependencies passed with no checks. # 'test_compile' -- check by making test program. # 'file_magic [[regex]]' -- check by looking for files in library path # that responds to the $file_magic_cmd with a given extended regex. # If you have 'file' or equivalent on your system and you're not sure # whether 'pass_all' will *always* work, you probably want this one. case $host_os in aix[[4-9]]*) lt_cv_deplibs_check_method=pass_all ;; beos*) lt_cv_deplibs_check_method=pass_all ;; bsdi[[45]]*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)' lt_cv_file_magic_cmd='/usr/bin/file -L' lt_cv_file_magic_test_file=/shlib/libc.so ;; cygwin*) # func_win32_libid is a shell function defined in ltmain.sh lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' ;; mingw* | pw32*) # Base MSYS/MinGW do not provide the 'file' command needed by # func_win32_libid shell function, so use a weaker test based on 'objdump', # unless we find 'file', for example because we are cross-compiling. if ( file / ) >/dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else # Keep this pattern in sync with the one in func_win32_libid. lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc*) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; haiku*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'] lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[[3-9]]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd* | bitrig*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; os2*) lt_cv_deplibs_check_method=pass_all ;; esac ]) file_magic_glob= want_nocaseglob=no if test "$build" = "$host"; then case $host_os in mingw* | pw32*) if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then want_nocaseglob=yes else file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"` fi ;; esac fi file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown _LT_DECL([], [deplibs_check_method], [1], [Method to check whether dependent libraries are shared objects]) _LT_DECL([], [file_magic_cmd], [1], [Command to use when deplibs_check_method = "file_magic"]) _LT_DECL([], [file_magic_glob], [1], [How to find potential files when deplibs_check_method = "file_magic"]) _LT_DECL([], [want_nocaseglob], [1], [Find potential files using nocaseglob when deplibs_check_method = "file_magic"]) ])# _LT_CHECK_MAGIC_METHOD # LT_PATH_NM # ---------- # find the pathname to a BSD- or MS-compatible name lister AC_DEFUN([LT_PATH_NM], [AC_REQUIRE([AC_PROG_CC])dnl AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, [if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM=$NM else lt_nm_to_check=${ac_tool_prefix}nm if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. tmp_nm=$ac_dir/$lt_tmp_nm if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then # Check to see if the nm accepts a BSD-compat flag. # Adding the 'sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty case $build_os in mingw*) lt_bad_file=conftest.nm/nofile ;; *) lt_bad_file=/dev/null ;; esac case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in *$lt_bad_file* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break 2 ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break 2 ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS=$lt_save_ifs done : ${lt_cv_path_NM=no} fi]) if test no != "$lt_cv_path_NM"; then NM=$lt_cv_path_NM else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$DUMPBIN"; then : # Let the user override the test. else AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols -headers" ;; *) DUMPBIN=: ;; esac fi AC_SUBST([DUMPBIN]) if test : != "$DUMPBIN"; then NM=$DUMPBIN fi fi test -z "$NM" && NM=nm AC_SUBST([NM]) _LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], [lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD) cat conftest.out >&AS_MESSAGE_LOG_FD if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest*]) ])# LT_PATH_NM # Old names: AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_PROG_NM], []) dnl AC_DEFUN([AC_PROG_NM], []) # _LT_CHECK_SHAREDLIB_FROM_LINKLIB # -------------------------------- # how to determine the name of the shared library # associated with a specific link library. # -- PORTME fill in with the dynamic library characteristics m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB], [m4_require([_LT_DECL_EGREP]) m4_require([_LT_DECL_OBJDUMP]) m4_require([_LT_DECL_DLLTOOL]) AC_CACHE_CHECK([how to associate runtime and link libraries], lt_cv_sharedlib_from_linklib_cmd, [lt_cv_sharedlib_from_linklib_cmd='unknown' case $host_os in cygwin* | mingw* | pw32* | cegcc*) # two different shell functions defined in ltmain.sh; # decide which one to use based on capabilities of $DLLTOOL case `$DLLTOOL --help 2>&1` in *--identify-strict*) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib ;; *) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback ;; esac ;; *) # fallback: assume linklib IS sharedlib lt_cv_sharedlib_from_linklib_cmd=$ECHO ;; esac ]) sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO _LT_DECL([], [sharedlib_from_linklib_cmd], [1], [Command to associate shared and link libraries]) ])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB # _LT_PATH_MANIFEST_TOOL # ---------------------- # locate the manifest tool m4_defun([_LT_PATH_MANIFEST_TOOL], [AC_CHECK_TOOL(MANIFEST_TOOL, mt, :) test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool], [lt_cv_path_mainfest_tool=no echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out cat conftest.err >&AS_MESSAGE_LOG_FD if $GREP 'Manifest Tool' conftest.out > /dev/null; then lt_cv_path_mainfest_tool=yes fi rm -f conftest*]) if test yes != "$lt_cv_path_mainfest_tool"; then MANIFEST_TOOL=: fi _LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl ])# _LT_PATH_MANIFEST_TOOL # _LT_DLL_DEF_P([FILE]) # --------------------- # True iff FILE is a Windows DLL '.def' file. # Keep in sync with func_dll_def_p in the libtool script AC_DEFUN([_LT_DLL_DEF_P], [dnl test DEF = "`$SED -n dnl -e '\''s/^[[ ]]*//'\'' dnl Strip leading whitespace -e '\''/^\(;.*\)*$/d'\'' dnl Delete empty lines and comments -e '\''s/^\(EXPORTS\|LIBRARY\)\([[ ]].*\)*$/DEF/p'\'' dnl -e q dnl Only consider the first "real" line $1`" dnl ])# _LT_DLL_DEF_P # LT_LIB_M # -------- # check for math library AC_DEFUN([LT_LIB_M], [AC_REQUIRE([AC_CANONICAL_HOST])dnl LIBM= case $host in *-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) # These system don't have libm, or don't need it ;; *-ncr-sysv4.3*) AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM=-lmw) AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") ;; *) AC_CHECK_LIB(m, cos, LIBM=-lm) ;; esac AC_SUBST([LIBM]) ])# LT_LIB_M # Old name: AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_CHECK_LIBM], []) # _LT_COMPILER_NO_RTTI([TAGNAME]) # ------------------------------- m4_defun([_LT_COMPILER_NO_RTTI], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= if test yes = "$GCC"; then case $cc_basename in nvcc*) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;; *) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;; esac _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], lt_cv_prog_compiler_rtti_exceptions, [-fno-rtti -fno-exceptions], [], [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) fi _LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], [Compiler flag to turn off builtin functions]) ])# _LT_COMPILER_NO_RTTI # _LT_CMD_GLOBAL_SYMBOLS # ---------------------- m4_defun([_LT_CMD_GLOBAL_SYMBOLS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([LT_PATH_NM])dnl AC_REQUIRE([LT_PATH_LD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_TAG_COMPILER])dnl # Check for command to grab the raw symbol name followed by C symbol from nm. AC_MSG_CHECKING([command to parse $NM output from $compiler object]) AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], [ # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[[BCDEGRST]]' # Regexp to match symbols that can be accessed directly from C. sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[[BCDT]]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[[ABCDGISTW]]' ;; hpux*) if test ia64 = "$host_cpu"; then symcode='[[ABCDEGRST]]' fi ;; irix* | nonstopux*) symcode='[[BCDEGRST]]' ;; osf*) symcode='[[BCDEGQRST]]' ;; solaris*) symcode='[[BDRT]]' ;; sco3.2v5*) symcode='[[DT]]' ;; sysv4.2uw2*) symcode='[[DT]]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[[ABDT]]' ;; sysv4) symcode='[[DFNSTU]]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[[ABCDGIRSTW]]' ;; esac if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Gets list of data symbols to import. lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" # Adjust the below global symbol transforms to fixup imported variables. lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" lt_c_name_lib_hook="\ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" else # Disable hooks by default. lt_cv_sys_global_symbol_to_import= lt_cdecl_hook= lt_c_name_hook= lt_c_name_lib_hook= fi # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="sed -n"\ $lt_cdecl_hook\ " -e 's/^T .* \(.*\)$/extern int \1();/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ $lt_c_name_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" # Transform an extracted symbol line into symbol name with lib prefix and # symbol address. lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ $lt_c_name_lib_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function, # D for any global variable and I for any imported variable. # Also find C++ and __fastcall symbols from MSVC++ or ICC, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK ['"\ " {last_section=section; section=\$ 3};"\ " /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ " /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ " /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ " {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ " s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx]" else lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if AC_TRY_EVAL(ac_compile); then # Now try to grab the symbols. nlist=conftest.nm if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT@&t@_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT@&t@_DLSYM_CONST #else # define LT@&t@_DLSYM_CONST const #endif #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ LT@&t@_DLSYM_CONST struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[[]] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_globsym_save_LIBS=$LIBS lt_globsym_save_CFLAGS=$CFLAGS LIBS=conftstm.$ac_objext CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" if AC_TRY_EVAL(ac_link) && test -s conftest$ac_exeext; then pipe_works=yes fi LIBS=$lt_globsym_save_LIBS CFLAGS=$lt_globsym_save_CFLAGS else echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD fi else echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test yes = "$pipe_works"; then break else lt_cv_sys_global_symbol_pipe= fi done ]) if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then AC_MSG_RESULT(failed) else AC_MSG_RESULT(ok) fi # Response file support. if test "$lt_cv_nm_interface" = "MS dumpbin"; then nm_file_list_spec='@' elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then nm_file_list_spec='@' fi _LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], [Take the output of nm and produce a listing of raw symbols and C names]) _LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], [Transform the output of nm in a proper C declaration]) _LT_DECL([global_symbol_to_import], [lt_cv_sys_global_symbol_to_import], [1], [Transform the output of nm into a list of symbols to manually relocate]) _LT_DECL([global_symbol_to_c_name_address], [lt_cv_sys_global_symbol_to_c_name_address], [1], [Transform the output of nm in a C name address pair]) _LT_DECL([global_symbol_to_c_name_address_lib_prefix], [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], [Transform the output of nm in a C name address pair when lib prefix is needed]) _LT_DECL([nm_interface], [lt_cv_nm_interface], [1], [The name lister interface]) _LT_DECL([], [nm_file_list_spec], [1], [Specify filename containing input files for $NM]) ]) # _LT_CMD_GLOBAL_SYMBOLS # _LT_COMPILER_PIC([TAGNAME]) # --------------------------- m4_defun([_LT_COMPILER_PIC], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_wl, $1)= _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)= m4_if([$1], [CXX], [ # C++ specific cases for pic, static, wl, etc. if test yes = "$GXX"; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; *djgpp*) # DJGPP does not support shared libraries at all _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac else case $host_os in aix[[4-9]]*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; chorus*) case $cc_basename in cxch68*) # Green Hills C++ Compiler # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" ;; esac ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; dgux*) case $cc_basename in ec++*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; ghcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; freebsd* | dragonfly*) # FreeBSD uses GNU C++ ;; hpux9* | hpux10* | hpux11*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' if test ia64 != "$host_cpu"; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' fi ;; aCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac ;; *) ;; esac ;; interix*) # This is c89, which is MS Visual C++ (no shared libs) # Anyone wants to do a port? ;; irix5* | irix6* | nonstopux*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' # CC pic flag -KPIC is the default. ;; *) ;; esac ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # KAI C++ Compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; ecpc* ) # old Intel C++ for x86_64, which still supported -KPIC. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; icpc* ) # Intel C++, used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; pgCC* | pgcpp*) # Portland Group C++ compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; cxx*) # Compaq C++ # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL 8.0, 9.0 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; esac ;; esac ;; lynxos*) ;; m88k*) ;; mvs*) case $cc_basename in cxx*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' ;; *) ;; esac ;; netbsd*) ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' ;; RCC*) # Rational C++ 2.4.1 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; cxx*) # Digital/Compaq C++ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; *) ;; esac ;; psos*) ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' ;; *) ;; esac ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; lcc*) # Lucid _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; *) ;; esac ;; vxworks*) ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ], [ if test yes = "$GCC"; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac case $cc_basename in nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker ' if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)" fi ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' case $cc_basename in nagfor*) # NAG Fortran compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; hpux9* | hpux10* | hpux11*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC (with -KPIC) is the default. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in # old Intel for x86_64, which still supported -KPIC. ecc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # Lahey Fortran 8.1. lf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' ;; nagfor*) # NAG Fortran compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; tcc*) # Fabrice Bellard et al's Tiny C Compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; ccc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All Alpha code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xl* | bgxl* | bgf* | mpixl*) # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='' ;; *Sun\ F* | *Sun*Fortran*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' ;; *Intel*\ [[CF]]*Compiler*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; *Portland\ Group*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; esac ;; newsos6) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All OSF/1 code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; rdos*) _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; solaris*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; *) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; esac ;; sunos4*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; unicos*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; uts4*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ]) case $host_os in # For platforms that do not support PIC, -DPIC is meaningless: *djgpp*) _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" ;; esac AC_CACHE_CHECK([for $compiler option to produce PIC], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) _LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1) # # Check to make sure the PIC flag actually works. # if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in "" | " "*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; esac], [_LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) fi _LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], [Additional compiler flags for building library objects]) _LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], [How to pass a linker flag through the compiler]) # # Check to make sure the static flag actually works. # wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" _LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), $lt_tmp_static_flag, [], [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) _LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], [Compiler flag to prevent dynamic linking]) ])# _LT_COMPILER_PIC # _LT_LINKER_SHLIBS([TAGNAME]) # ---------------------------- # See if the linker supports building shared libraries. m4_defun([_LT_LINKER_SHLIBS], [AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) m4_if([$1], [CXX], [ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] case $host_os in aix[[4-9]]*) # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "L") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi ;; pw32*) _LT_TAGVAR(export_symbols_cmds, $1)=$ltdll_cmds ;; cygwin* | mingw* | cegcc*) case $cc_basename in cl* | icl*) _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] ;; esac ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' ;; esac ], [ runpath_var= _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_cmds, $1)= _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(old_archive_from_new_cmds, $1)= _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= _LT_TAGVAR(thread_safe_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list _LT_TAGVAR(include_expsyms, $1)= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ' (' and ')$', so one must not match beginning or # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', # as well as any symbol that contains 'd'. _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. dnl Note also adjust exclude_expsyms for C++ above. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ and ICC port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++ or Intel C++ Compiler. if test yes != "$GCC"; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++ or ICC) with_gnu_ld=yes ;; openbsd* | bitrig*) with_gnu_ld=no ;; esac _LT_TAGVAR(ld_shlibs, $1)=yes # On some targets, GNU ld is compatible enough with the native linker # that we're better off using the native interface for both. lt_use_gnu_ld_interface=no if test yes = "$with_gnu_ld"; then case $host_os in aix*) # The AIX port of GNU ld has always aspired to compatibility # with the native linker. However, as the warning in the GNU ld # block says, versions before 2.19.5* couldn't really create working # shared libraries, regardless of the interface used. case `$LD -v 2>&1` in *\ \(GNU\ Binutils\)\ 2.19.5*) ;; *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;; *\ \(GNU\ Binutils\)\ [[3-9]]*) ;; *) lt_use_gnu_ld_interface=yes ;; esac ;; *) lt_use_gnu_ld_interface=yes ;; esac fi if test yes = "$lt_use_gnu_ld_interface"; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='$wl' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi supports_anon_versioning=no case `$LD -v | $SED -e 's/([^)]\+)\s\+//' 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[[3-9]]*) # On AIX/PPC, the GNU linker is very broken if test ia64 != "$host_cpu"; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.19, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to install binutils *** 2.20 or above, or modify your PATH so that a non-GNU linker is found. *** You will then need to restart the configuration process. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test linux-dietlibc = "$host_os"; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test no = "$tmp_diet" then tmp_addflag=' $pic_flag' tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group f77 and f90 compilers _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 _LT_TAGVAR(whole_archive_flag_spec, $1)= tmp_sharedflag='--shared' ;; nagfor*) # NAGFOR 5.3 tmp_sharedflag='-Wl,-shared' ;; xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes ;; esac case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi case $cc_basename in tcc*) _LT_TAGVAR(export_dynamic_flag_spec, $1)='-rdynamic' ;; xlf* | bgf* | bgxlf* | mpixlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; sunos4*) _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac if test no = "$_LT_TAGVAR(ld_shlibs, $1)"; then runpath_var= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. _LT_TAGVAR(hardcode_minus_L, $1)=yes if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. _LT_TAGVAR(hardcode_direct, $1)=unsupported fi ;; aix[[4-9]]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "L") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then aix_use_runtimelinking=yes break fi done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # traditional, no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no ;; esac if test yes = "$GCC"; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi ;; esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag="$shared_flag "'$wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. _LT_TAGVAR(always_export_symbols, $1)=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. _LT_TAGVAR(allow_undefined_flag, $1)='-berok' # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared libraries. _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; bsdi[[45]]*) _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++ or Intel C++ Compiler. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in cl* | icl*) # Native MSVC or ICC _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # Assume MSVC and ICC wrapper _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' # FIXME: Should let the user specify the lib program. _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; dgux*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2.*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; hpux9*) if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' ;; hpux10*) if test yes,no = "$GCC,$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes fi ;; hpux11*) if test yes,no = "$GCC,$with_gnu_ld"; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) m4_if($1, [], [ # Older versions of the 11.00 compiler do not understand -b yet # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) _LT_LINKER_OPTION([if $CC understands -b], _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b], [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])], [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) ;; esac fi if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. # This should be the same for all languages, so no per-tag cache variable. AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol], [lt_cv_irix_exported_symbol], [save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" AC_LINK_IFELSE( [AC_LANG_SOURCE( [AC_LANG_CASE([C], [[int foo (void) { return 0; }]], [C++], [[int foo (void) { return 0; }]], [Fortran 77], [[ subroutine foo end]], [Fortran], [[ subroutine foo end]])])], [lt_cv_irix_exported_symbol=yes], [lt_cv_irix_exported_symbol=no]) LDFLAGS=$save_LDFLAGS]) if test yes = "$lt_cv_irix_exported_symbol"; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' fi else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes _LT_TAGVAR(link_all_deplibs, $1)=yes ;; linux*) case $cc_basename in tcc*) # Fabrice Bellard et al's Tiny C Compiler _LT_TAGVAR(ld_shlibs, $1)=yes _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; newsos6) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *nto* | *qnx*) ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' fi else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' ;; osf3*) if test yes = "$GCC"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test yes = "$GCC"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; solaris*) _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' if test yes = "$GCC"; then wlarc='$wl' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' _LT_TAGVAR(archive_cmds, $1)='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='$wl' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. GCC discards it without '$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test yes = "$GCC"; then _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' else _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' fi ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes ;; sunos4*) if test sequent = "$host_vendor"; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4) case $host_vendor in sni) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' _LT_TAGVAR(hardcode_direct, $1)=no ;; motorola) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4.3*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes _LT_TAGVAR(ld_shlibs, $1)=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(ld_shlibs, $1)=no ;; esac if test sni = "$host_vendor"; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Blargedynsym' ;; esac fi fi ]) AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no _LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld _LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl _LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl _LT_DECL([], [extract_expsyms_cmds], [2], [The commands to extract the exported symbol list from a shared archive]) # # Do we need to explicitly link libc? # case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in x|xyes) # Assume -lc should be added _LT_TAGVAR(archive_cmds_need_lc, $1)=yes if test yes,yes = "$GCC,$enable_shared"; then case $_LT_TAGVAR(archive_cmds, $1) in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. AC_CACHE_CHECK([whether -lc should be explicitly linked in], [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1), [$RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if AC_TRY_EVAL(ac_compile) 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) _LT_TAGVAR(allow_undefined_flag, $1)= if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) then lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no else lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes fi _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* ]) _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1) ;; esac fi ;; esac _LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], [Whether or not to add -lc for building shared libraries]) _LT_TAGDECL([allow_libtool_libs_with_static_runtimes], [enable_shared_with_static_runtimes], [0], [Whether or not to disallow shared libs when runtime libs are static]) _LT_TAGDECL([], [export_dynamic_flag_spec], [1], [Compiler flag to allow reflexive dlopens]) _LT_TAGDECL([], [whole_archive_flag_spec], [1], [Compiler flag to generate shared objects directly from archives]) _LT_TAGDECL([], [compiler_needs_object], [1], [Whether the compiler copes with passing no objects directly]) _LT_TAGDECL([], [old_archive_from_new_cmds], [2], [Create an old-style archive from a shared archive]) _LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], [Create a temporary old-style archive to link instead of a shared archive]) _LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) _LT_TAGDECL([], [archive_expsym_cmds], [2]) _LT_TAGDECL([], [module_cmds], [2], [Commands used to build a loadable module if different from building a shared archive.]) _LT_TAGDECL([], [module_expsym_cmds], [2]) _LT_TAGDECL([], [with_gnu_ld], [1], [Whether we are building with GNU ld or not]) _LT_TAGDECL([], [allow_undefined_flag], [1], [Flag that allows shared libraries with undefined symbols to be built]) _LT_TAGDECL([], [no_undefined_flag], [1], [Flag that enforces no undefined symbols]) _LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], [Flag to hardcode $libdir into a binary during linking. This must work even if $libdir does not exist]) _LT_TAGDECL([], [hardcode_libdir_separator], [1], [Whether we need a single "-rpath" flag with a separated argument]) _LT_TAGDECL([], [hardcode_direct], [0], [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_direct_absolute], [0], [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes DIR into the resulting binary and the resulting library dependency is "absolute", i.e impossible to change by setting $shlibpath_var if the library is relocated]) _LT_TAGDECL([], [hardcode_minus_L], [0], [Set to "yes" if using the -LDIR flag during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_shlibpath_var], [0], [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_automatic], [0], [Set to "yes" if building a shared library automatically hardcodes DIR into the library and all subsequent libraries and executables linked against it]) _LT_TAGDECL([], [inherit_rpath], [0], [Set to yes if linker adds runtime paths of dependent libraries to runtime path list]) _LT_TAGDECL([], [link_all_deplibs], [0], [Whether libtool must link a program against all its dependency libraries]) _LT_TAGDECL([], [always_export_symbols], [0], [Set to "yes" if exported symbols are required]) _LT_TAGDECL([], [export_symbols_cmds], [2], [The commands to list exported symbols]) _LT_TAGDECL([], [exclude_expsyms], [1], [Symbols that should not be listed in the preloaded symbols]) _LT_TAGDECL([], [include_expsyms], [1], [Symbols that must always be exported]) _LT_TAGDECL([], [prelink_cmds], [2], [Commands necessary for linking programs (against libraries) with templates]) _LT_TAGDECL([], [postlink_cmds], [2], [Commands necessary for finishing linking programs]) _LT_TAGDECL([], [file_list_spec], [1], [Specify filename containing input files]) dnl FIXME: Not yet implemented dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], dnl [Compiler flag to generate thread safe objects]) ])# _LT_LINKER_SHLIBS # _LT_LANG_C_CONFIG([TAG]) # ------------------------ # Ensure that the configuration variables for a C compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to 'libtool'. m4_defun([_LT_LANG_C_CONFIG], [m4_require([_LT_DECL_EGREP])dnl lt_save_CC=$CC AC_LANG_PUSH(C) # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' _LT_TAG_COMPILER # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) LT_SYS_DLOPEN_SELF _LT_CMD_STRIPLIB # Report what library types will actually be built AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_CONFIG($1) fi AC_LANG_POP CC=$lt_save_CC ])# _LT_LANG_C_CONFIG # _LT_LANG_CXX_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a C++ compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to 'libtool'. m4_defun([_LT_LANG_CXX_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl if test -n "$CXX" && ( test no != "$CXX" && ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) || (test g++ != "$CXX"))); then AC_PROG_CXXCPP else _lt_caught_CXX_error=yes fi AC_LANG_PUSH(C++) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for C++ test sources. ac_ext=cpp # Object file extension for compiled C++ test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the CXX compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_caught_CXX_error"; then # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_LD=$LD lt_save_GCC=$GCC GCC=$GXX lt_save_with_gnu_ld=$with_gnu_ld lt_save_path_LD=$lt_cv_path_LD if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx else $as_unset lt_cv_prog_gnu_ld fi if test -n "${lt_cv_path_LDCXX+set}"; then lt_cv_path_LD=$lt_cv_path_LDCXX else $as_unset lt_cv_path_LD fi test -z "${LDCXX+set}" || LD=$LDCXX CC=${CXX-"c++"} CFLAGS=$CXXFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then # We don't want -fno-exception when compiling C++ code, so set the # no_builtin_flag separately if test yes = "$GXX"; then _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' else _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= fi if test yes = "$GXX"; then # Set up default GNU C++ configuration LT_PATH_LD # Check if GNU C++ uses GNU ld as the underlying linker, since the # archiving commands below assume that GNU ld is being used. if test yes = "$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # If archive_cmds runs LD, not CC, wlarc should be empty # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to # investigate it a little bit more. (MM) wlarc='$wl' # ancient GNU ld didn't support --whole-archive et. al. if eval "`$CC -print-prog-name=ld` --help 2>&1" | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else with_gnu_ld=no wlarc= # A generic and very simple default shared library creation # command for GNU C++ for the case where it uses the native # linker, instead of GNU ld. If possible, this setting should # overridden to take advantage of the native linker features on # the platform it is being used on. _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' fi # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else GXX=no with_gnu_ld=no wlarc= fi # PORTME: fill in a description of your system's C++ link characteristics AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) _LT_TAGVAR(ld_shlibs, $1)=yes case $host_os in aix3*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aix[[4-9]]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do case $ld_flag in *-brtl*) aix_use_runtimelinking=yes break ;; esac done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no ;; esac if test yes = "$GXX"; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag=$shared_flag' $wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to # export. _LT_TAGVAR(always_export_symbols, $1)=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. # The "-G" linker flag allows undefined symbols. _LT_TAGVAR(no_undefined_flag, $1)='-bernotok' # Determine the default libpath from the value encoded in an empty # executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' fi fi ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; chorus*) case $cc_basename in *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; cygwin* | mingw* | pw32* | cegcc*) case $GXX,$cc_basename in ,cl* | no,cl* | ,icl* | no,icl*) # Native MSVC or ICC # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ func_to_tool_file "$lt_outputfile"~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # g++ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' ;; dgux*) case $cc_basename in ec++*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; ghcx*) # Green Hills C++ Compiler # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; freebsd2.*) # C++ shared libraries reported to be fairly broken before # switch to ELF _LT_TAGVAR(ld_shlibs, $1)=no ;; freebsd-elf*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; freebsd* | dragonfly*) # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF # conventions _LT_TAGVAR(ld_shlibs, $1)=yes ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; hpux9*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; hpux10*|hpux11*) if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) ;; *) _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' ;; esac fi case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. ;; esac case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac fi else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; irix5* | irix6*) case $cc_basename in CC*) # SGI C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' # Archives containing C++ object files must be created using # "CC -ar", where "CC" is the IRIX C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib' fi fi _LT_TAGVAR(link_all_deplibs, $1)=yes ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # Archives containing C++ object files must be created using # "CC -Bstatic", where "CC" is the KAI C++ compiler. _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; icpc* | ecpc* ) # Intel C++ with_gnu_ld=yes # version 8.0 and above of icpc choke on multiply defined symbols # if we add $predep_objects and $postdep_objects, however 7.1 and # earlier do not add the objects themselves. case `$CC -V 2>&1` in *"Version 7."*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 8.0 or newer tmp_idyn= case $host_cpu in ia64*) tmp_idyn=' -i_dynamic';; esac _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' ;; pgCC* | pgcpp*) # Portland Group C++ compiler case `$CC -V` in *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*) _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ $RANLIB $oldlib' _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 6 and above use weak symbols _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl--rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' ;; cxx*) # Compaq C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols' runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' ;; xl* | mpixl* | bgxl*) # IBM XL 8.0 on PPC, with GNU ld _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes # Not sure whether something based on # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 # would be better. output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; esac ;; esac ;; lynxos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; m88k*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; mvs*) case $cc_basename in cxx*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' wlarc= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no fi # Workaround some broken pre-1.5 toolchains output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' ;; *nto* | *qnx*) _LT_TAGVAR(ld_shlibs, $1)=yes ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' fi output_verbose_link_cmd=func_echo_all else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Archives containing C++ object files must be created using # the KAI C++ compiler. case $host in osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; esac ;; RCC*) # Rational C++ 2.4.1 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; cxx*) case $host in osf3*) _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' ;; *) _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ echo "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~ $RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' ;; esac _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes,no = "$GXX,$with_gnu_ld"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' case $host in osf3*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; psos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; lcc*) # Lucid # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(archive_cmds_need_lc,$1)=yes _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. # Supported since Solaris 2.6 (maybe 2.5.1?) _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' # The C++ compiler must be used to create the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' ;; *) # GNU C++ compiler with Solaris linker if test yes,no = "$GXX,$with_gnu_ld"; then _LT_TAGVAR(no_undefined_flag, $1)=' $wl-z ${wl}defs' if $CC --version | $GREP -v '^2\.7' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # g++ 2.7 appears to require '-G' NOT '-shared' on this # platform. _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $wl$libdir' case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' ;; esac fi ;; esac ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~ '"$_LT_TAGVAR(old_archive_cmds, $1)" _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~ '"$_LT_TAGVAR(reload_cmds, $1)" ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; vxworks*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no _LT_TAGVAR(GCC, $1)=$GXX _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS LDCXX=$LD LD=$lt_save_LD GCC=$lt_save_GCC with_gnu_ld=$lt_save_with_gnu_ld lt_cv_path_LDCXX=$lt_cv_path_LD lt_cv_path_LD=$lt_save_path_LD lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld fi # test yes != "$_lt_caught_CXX_error" AC_LANG_POP ])# _LT_LANG_CXX_CONFIG # _LT_FUNC_STRIPNAME_CNF # ---------------------- # func_stripname_cnf prefix suffix name # strip PREFIX and SUFFIX off of NAME. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). # # This function is identical to the (non-XSI) version of func_stripname, # except this one can be used by m4 code that may be executed by configure, # rather than the libtool script. m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl AC_REQUIRE([_LT_DECL_SED]) AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH]) func_stripname_cnf () { case @S|@2 in .*) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%\\\\@S|@2\$%%"`;; *) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%@S|@2\$%%"`;; esac } # func_stripname_cnf ])# _LT_FUNC_STRIPNAME_CNF # _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) # --------------------------------- # Figure out "hidden" library dependencies from verbose # compiler output when linking a shared library. # Parse the compiler output and extract the necessary # objects, libraries and library flags. m4_defun([_LT_SYS_HIDDEN_LIBDEPS], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl # Dependencies to place before and after the object being linked: _LT_TAGVAR(predep_objects, $1)= _LT_TAGVAR(postdep_objects, $1)= _LT_TAGVAR(predeps, $1)= _LT_TAGVAR(postdeps, $1)= _LT_TAGVAR(compiler_lib_search_path, $1)= dnl we can't use the lt_simple_compile_test_code here, dnl because it contains code intended for an executable, dnl not a library. It's possible we should let each dnl tag define a new lt_????_link_test_code variable, dnl but it's only used here... m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF int a; void foo (void) { a = 0; } _LT_EOF ], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF class Foo { public: Foo (void) { a = 0; } private: int a; }; _LT_EOF ], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer*4 a a=0 return end _LT_EOF ], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer a a=0 return end _LT_EOF ], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF public class foo { private int a; public void bar (void) { a = 0; } }; _LT_EOF ], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF package foo func foo() { } _LT_EOF ]) _lt_libdeps_save_CFLAGS=$CFLAGS case "$CC $CFLAGS " in #( *\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; *\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; *\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; esac dnl Parse the compiler output and extract the necessary dnl objects, libraries and library flags. if AC_TRY_EVAL(ac_compile); then # Parse the compiler output and extract the necessary # objects, libraries and library flags. # Sentinel used to keep track of whether or not we are before # the conftest object file. pre_test_object_deps_done=no for p in `eval "$output_verbose_link_cmd"`; do case $prev$p in -L* | -R* | -l*) # Some compilers place space between "-{L,R}" and the path. # Remove the space. if test x-L = "$p" || test x-R = "$p"; then prev=$p continue fi # Expand the sysroot to ease extracting the directories later. if test -z "$prev"; then case $p in -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; esac fi case $p in =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; esac if test no = "$pre_test_object_deps_done"; then case $prev in -L | -R) # Internal compiler library paths should come after those # provided the user. The postdeps already come after the # user supplied libs so there is no need to process them. if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then _LT_TAGVAR(compiler_lib_search_path, $1)=$prev$p else _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} $prev$p" fi ;; # The "-l" case would never come before the object being # linked, so don't bother handling this case. esac else if test -z "$_LT_TAGVAR(postdeps, $1)"; then _LT_TAGVAR(postdeps, $1)=$prev$p else _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} $prev$p" fi fi prev= ;; *.lto.$objext) ;; # Ignore GCC LTO objects *.$objext) # This assumes that the test object file only shows up # once in the compiler output. if test "$p" = "conftest.$objext"; then pre_test_object_deps_done=yes continue fi if test no = "$pre_test_object_deps_done"; then if test -z "$_LT_TAGVAR(predep_objects, $1)"; then _LT_TAGVAR(predep_objects, $1)=$p else _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" fi else if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then _LT_TAGVAR(postdep_objects, $1)=$p else _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" fi fi ;; *) ;; # Ignore the rest. esac done # Clean up. rm -f a.out a.exe else echo "libtool.m4: error: problem compiling $1 test program" fi $RM -f confest.$objext CFLAGS=$_lt_libdeps_save_CFLAGS # PORTME: override above test on systems where it is broken m4_if([$1], [CXX], [case $host_os in interix[[3-9]]*) # Interix 3.5 installs completely hosed .la files for C++, so rather than # hack all around it, let's just trust "g++" to DTRT. _LT_TAGVAR(predep_objects,$1)= _LT_TAGVAR(postdep_objects,$1)= _LT_TAGVAR(postdeps,$1)= ;; esac ]) case " $_LT_TAGVAR(postdeps, $1) " in *" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; esac _LT_TAGVAR(compiler_lib_search_dirs, $1)= if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | $SED -e 's! -L! !g' -e 's!^ !!'` fi _LT_TAGDECL([], [compiler_lib_search_dirs], [1], [The directories searched by this compiler when creating a shared library]) _LT_TAGDECL([], [predep_objects], [1], [Dependencies to place before and after the objects being linked to create a shared library]) _LT_TAGDECL([], [postdep_objects], [1]) _LT_TAGDECL([], [predeps], [1]) _LT_TAGDECL([], [postdeps], [1]) _LT_TAGDECL([], [compiler_lib_search_path], [1], [The library search path used internally by the compiler when linking a shared library]) ])# _LT_SYS_HIDDEN_LIBDEPS # _LT_LANG_F77_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a Fortran 77 compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_F77_CONFIG], [AC_LANG_PUSH(Fortran 77) if test -z "$F77" || test no = "$F77"; then _lt_disable_F77=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for f77 test sources. ac_ext=f # Object file extension for compiled f77 test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the F77 compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_disable_F77"; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${F77-"f77"} CFLAGS=$FFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) GCC=$G77 if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)=$G77 _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS fi # test yes != "$_lt_disable_F77" AC_LANG_POP ])# _LT_LANG_F77_CONFIG # _LT_LANG_FC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for a Fortran compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_FC_CONFIG], [AC_LANG_PUSH(Fortran) if test -z "$FC" || test no = "$FC"; then _lt_disable_FC=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for fc test sources. ac_ext=${ac_fc_srcext-f} # Object file extension for compiled fc test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the FC compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_disable_FC"; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${FC-"f95"} CFLAGS=$FCFLAGS compiler=$CC GCC=$ac_cv_fc_compiler_gnu _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)=$ac_cv_fc_compiler_gnu _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS fi # test yes != "$_lt_disable_FC" AC_LANG_POP ])# _LT_LANG_FC_CONFIG # _LT_LANG_GCJ_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Java Compiler compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_GCJ_CONFIG], [AC_REQUIRE([LT_PROG_GCJ])dnl AC_LANG_SAVE # Source file extension for Java test sources. ac_ext=java # Object file extension for compiled Java test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="class foo {}" # Code to be used in simple link tests lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GCJ-"gcj"} CFLAGS=$GCJFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)=$LD _LT_CC_BASENAME([$compiler]) # GCJ did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GCJ_CONFIG # _LT_LANG_GO_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Go compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_GO_CONFIG], [AC_REQUIRE([LT_PROG_GO])dnl AC_LANG_SAVE # Source file extension for Go test sources. ac_ext=go # Object file extension for compiled Go test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="package main; func main() { }" # Code to be used in simple link tests lt_simple_link_test_code='package main; func main() { }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GOC-"gccgo"} CFLAGS=$GOFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)=$LD _LT_CC_BASENAME([$compiler]) # Go did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GO_CONFIG # _LT_LANG_RC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for the Windows resource compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_RC_CONFIG], [AC_REQUIRE([LT_PROG_RC])dnl AC_LANG_SAVE # Source file extension for RC test sources. ac_ext=rc # Object file extension for compiled RC test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' # Code to be used in simple link tests lt_simple_link_test_code=$lt_simple_compile_test_code # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC= CC=${RC-"windres"} CFLAGS= compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes if test -n "$compiler"; then : _LT_CONFIG($1) fi GCC=$lt_save_GCC AC_LANG_RESTORE CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_RC_CONFIG # LT_PROG_GCJ # ----------- AC_DEFUN([LT_PROG_GCJ], [m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], [AC_CHECK_TOOL(GCJ, gcj,) test set = "${GCJFLAGS+set}" || GCJFLAGS="-g -O2" AC_SUBST(GCJFLAGS)])])[]dnl ]) # Old name: AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_GCJ], []) # LT_PROG_GO # ---------- AC_DEFUN([LT_PROG_GO], [AC_CHECK_TOOL(GOC, gccgo,) ]) # LT_PROG_RC # ---------- AC_DEFUN([LT_PROG_RC], [AC_CHECK_TOOL(RC, windres,) ]) # Old name: AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_RC], []) # _LT_DECL_EGREP # -------------- # If we don't have a new enough Autoconf to choose the best grep # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_EGREP], [AC_REQUIRE([AC_PROG_EGREP])dnl AC_REQUIRE([AC_PROG_FGREP])dnl test -z "$GREP" && GREP=grep _LT_DECL([], [GREP], [1], [A grep program that handles long lines]) _LT_DECL([], [EGREP], [1], [An ERE matcher]) _LT_DECL([], [FGREP], [1], [A literal string matcher]) dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too AC_SUBST([GREP]) ]) # _LT_DECL_OBJDUMP # -------------- # If we don't have a new enough Autoconf to choose the best objdump # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_OBJDUMP], [AC_CHECK_TOOL(OBJDUMP, objdump, false) test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) AC_SUBST([OBJDUMP]) ]) # _LT_DECL_DLLTOOL # ---------------- # Ensure DLLTOOL variable is set. m4_defun([_LT_DECL_DLLTOOL], [AC_CHECK_TOOL(DLLTOOL, dlltool, false) test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [1], [DLL creation program]) AC_SUBST([DLLTOOL]) ]) # _LT_DECL_SED # ------------ # Check for a fully-functional sed program, that truncates # as few characters as possible. Prefer GNU sed if found. m4_defun([_LT_DECL_SED], [AC_PROG_SED test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" _LT_DECL([], [SED], [1], [A sed program that does not truncate output]) _LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], [Sed that helps us avoid accidentally triggering echo(1) options like -n]) ])# _LT_DECL_SED m4_ifndef([AC_PROG_SED], [ ############################################################ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_SED. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # ############################################################ m4_defun([AC_PROG_SED], [AC_MSG_CHECKING([for a sed that does not truncate output]) AC_CACHE_VAL(lt_cv_path_SED, [# Loop through the user's path and test for sed and gsed. # Then use that list of sed's as ones to test for truncation. as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for lt_ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" fi done done done IFS=$as_save_IFS lt_ac_max=0 lt_ac_count=0 # Add /usr/xpg4/bin/sed as it is typically found on Solaris # along with /bin/sed that truncates output. for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do test ! -f "$lt_ac_sed" && continue cat /dev/null > conftest.in lt_ac_count=0 echo $ECHO_N "0123456789$ECHO_C" >conftest.in # Check for GNU sed and select it if it is found. if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then lt_cv_path_SED=$lt_ac_sed break fi while true; do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo >>conftest.nl $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break cmp -s conftest.out conftest.nl || break # 10000 chars as input seems more than enough test 10 -lt "$lt_ac_count" && break lt_ac_count=`expr $lt_ac_count + 1` if test "$lt_ac_count" -gt "$lt_ac_max"; then lt_ac_max=$lt_ac_count lt_cv_path_SED=$lt_ac_sed fi done done ]) SED=$lt_cv_path_SED AC_SUBST([SED]) AC_MSG_RESULT([$SED]) ])#AC_PROG_SED ])#m4_ifndef # Old name: AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_SED], []) # _LT_CHECK_SHELL_FEATURES # ------------------------ # Find out whether the shell is Bourne or XSI compatible, # or has some other useful features. m4_defun([_LT_CHECK_SHELL_FEATURES], [if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi _LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac _LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl _LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl ])# _LT_CHECK_SHELL_FEATURES # _LT_PATH_CONVERSION_FUNCTIONS # ----------------------------- # Determine what file name conversion functions should be used by # func_to_host_file (and, implicitly, by func_to_host_path). These are needed # for certain cross-compile configurations and native mingw. m4_defun([_LT_PATH_CONVERSION_FUNCTIONS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_MSG_CHECKING([how to convert $build file names to $host format]) AC_CACHE_VAL(lt_cv_to_host_file_cmd, [case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 ;; esac ;; *-*-cygwin* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_noop ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin ;; esac ;; * ) # unhandled hosts (and "normal" native builds) lt_cv_to_host_file_cmd=func_convert_file_noop ;; esac ]) to_host_file_cmd=$lt_cv_to_host_file_cmd AC_MSG_RESULT([$lt_cv_to_host_file_cmd]) _LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd], [0], [convert $build file names to $host format])dnl AC_MSG_CHECKING([how to convert $build file names to toolchain format]) AC_CACHE_VAL(lt_cv_to_tool_file_cmd, [#assume ordinary cross tools, or native build. lt_cv_to_tool_file_cmd=func_convert_file_noop case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 ;; esac ;; esac ]) to_tool_file_cmd=$lt_cv_to_tool_file_cmd AC_MSG_RESULT([$lt_cv_to_tool_file_cmd]) _LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd], [0], [convert $build files to toolchain format])dnl ])# _LT_PATH_CONVERSION_FUNCTIONS wimlib-1.13.1/m4/lt~obsolete.m40000644000175000017500000001377413427652374013166 00000000000000# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- # # Copyright (C) 2004-2005, 2007, 2009, 2011-2018 Free Software # Foundation, Inc. # Written by Scott James Remnant, 2004. # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 5 lt~obsolete.m4 # These exist entirely to fool aclocal when bootstrapping libtool. # # In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN), # which have later been changed to m4_define as they aren't part of the # exported API, or moved to Autoconf or Automake where they belong. # # The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN # in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us # using a macro with the same name in our local m4/libtool.m4 it'll # pull the old libtool.m4 in (it doesn't see our shiny new m4_define # and doesn't know about Autoconf macros at all.) # # So we provide this file, which has a silly filename so it's always # included after everything else. This provides aclocal with the # AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything # because those macros already exist, or will be overwritten later. # We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. # # Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. # Yes, that means every name once taken will need to remain here until # we give up compatibility with versions before 1.7, at which point # we need to keep only those names which we still refer to. # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])]) m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])]) m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])]) m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])]) m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) wimlib-1.13.1/m4/ltsugar.m40000644000175000017500000001044013427652374012260 00000000000000# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- # # Copyright (C) 2004-2005, 2007-2008, 2011-2018 Free Software # Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 6 ltsugar.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) # lt_join(SEP, ARG1, [ARG2...]) # ----------------------------- # Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their # associated separator. # Needed until we can rely on m4_join from Autoconf 2.62, since all earlier # versions in m4sugar had bugs. m4_define([lt_join], [m4_if([$#], [1], [], [$#], [2], [[$2]], [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) m4_define([_lt_join], [m4_if([$#$2], [2], [], [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) # lt_car(LIST) # lt_cdr(LIST) # ------------ # Manipulate m4 lists. # These macros are necessary as long as will still need to support # Autoconf-2.59, which quotes differently. m4_define([lt_car], [[$1]]) m4_define([lt_cdr], [m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], [$#], 1, [], [m4_dquote(m4_shift($@))])]) m4_define([lt_unquote], $1) # lt_append(MACRO-NAME, STRING, [SEPARATOR]) # ------------------------------------------ # Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'. # Note that neither SEPARATOR nor STRING are expanded; they are appended # to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). # No SEPARATOR is output if MACRO-NAME was previously undefined (different # than defined and empty). # # This macro is needed until we can rely on Autoconf 2.62, since earlier # versions of m4sugar mistakenly expanded SEPARATOR but not STRING. m4_define([lt_append], [m4_define([$1], m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) # lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) # ---------------------------------------------------------- # Produce a SEP delimited list of all paired combinations of elements of # PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list # has the form PREFIXmINFIXSUFFIXn. # Needed until we can rely on m4_combine added in Autoconf 2.62. m4_define([lt_combine], [m4_if(m4_eval([$# > 3]), [1], [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl [[m4_foreach([_Lt_prefix], [$2], [m4_foreach([_Lt_suffix], ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) # lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) # ----------------------------------------------------------------------- # Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited # by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. m4_define([lt_if_append_uniq], [m4_ifdef([$1], [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], [lt_append([$1], [$2], [$3])$4], [$5])], [lt_append([$1], [$2], [$3])$4])]) # lt_dict_add(DICT, KEY, VALUE) # ----------------------------- m4_define([lt_dict_add], [m4_define([$1($2)], [$3])]) # lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) # -------------------------------------------- m4_define([lt_dict_add_subkey], [m4_define([$1($2:$3)], [$4])]) # lt_dict_fetch(DICT, KEY, [SUBKEY]) # ---------------------------------- m4_define([lt_dict_fetch], [m4_ifval([$3], m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) # lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) # ----------------------------------------------------------------- m4_define([lt_if_dict_fetch], [m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], [$5], [$6])]) # lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) # -------------------------------------------------------------- m4_define([lt_dict_filter], [m4_if([$5], [], [], [lt_join(m4_quote(m4_default([$4], [[, ]])), lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl ]) wimlib-1.13.1/m4/ltversion.m40000644000175000017500000000132313427652374012624 00000000000000# ltversion.m4 -- version numbers -*- Autoconf -*- # # Copyright (C) 2004, 2011-2018 Free Software Foundation, Inc. # Written by Scott James Remnant, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # @configure_input@ # serial 4221 ltversion.m4 # This file is part of GNU Libtool m4_define([LT_PACKAGE_VERSION], [2.4.6.42-b88ce]) m4_define([LT_PACKAGE_REVISION], [2.4.6.42]) AC_DEFUN([LTVERSION_VERSION], [macro_version='2.4.6.42-b88ce' macro_revision='2.4.6.42' _LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) _LT_DECL(, macro_revision, 0) ]) wimlib-1.13.1/m4/ax_pthread.m40000644000175000017500000003267613016523676012731 00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_pthread.html # =========================================================================== # # SYNOPSIS # # AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) # # DESCRIPTION # # This macro figures out how to build C programs using POSIX threads. It # sets the PTHREAD_LIBS output variable to the threads library and linker # flags, and the PTHREAD_CFLAGS output variable to any special C compiler # flags that are needed. (The user can also force certain compiler # flags/libs to be tested by setting these environment variables.) # # Also sets PTHREAD_CC to any special C compiler that is needed for # multi-threaded programs (defaults to the value of CC otherwise). (This # is necessary on AIX to use the special cc_r compiler alias.) # # NOTE: You are assumed to not only compile your program with these flags, # but also link it with them as well. e.g. you should link with # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # # If you are only building threads programs, you may wish to use these # variables in your default LIBS, CFLAGS, and CC: # # LIBS="$PTHREAD_LIBS $LIBS" # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # CC="$PTHREAD_CC" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant # has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name # (e.g. PTHREAD_CREATE_UNDETACHED on AIX). # # Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the # PTHREAD_PRIO_INHERIT symbol is defined when compiling with # PTHREAD_CFLAGS. # # ACTION-IF-FOUND is a list of shell commands to run if a threads library # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it # is not found. If ACTION-IF-FOUND is not specified, the default action # will define HAVE_PTHREAD. # # Please let the authors know if this macro fails on any platform, or if # you have any other suggestions or comments. This macro was based on work # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by # Alejandro Forero Cuervo to the autoconf macro repository. We are also # grateful for the helpful feedback of numerous users. # # Updated for Autoconf 2.68 by Daniel Richard G. # # LICENSE # # Copyright (c) 2008 Steven G. Johnson # Copyright (c) 2011 Daniel Richard G. # # 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 # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 21 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AC_DEFUN([AX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_LANG_PUSH([C]) ax_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on True64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) AC_TRY_LINK_FUNC([pthread_join], [ax_pthread_ok=yes]) AC_MSG_RESULT([$ax_pthread_ok]) if test x"$ax_pthread_ok" = xno; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) # -pthreads: Solaris/gcc # -mthreads: Mingw32/gcc, Lynx/gcc # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads too; # also defines -D_REENTRANT) # ... -mt is also the pthreads flag for HP/aCC # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case ${host_os} in solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (We need to link with -pthreads/-mt/ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather # a function called by this macro, so we could check for that, but # who knows whether they'll stub that too in a future libc.) So, # we'll just look for -pthreads and -lpthread first: ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" ;; darwin*) ax_pthread_flags="-pthread $ax_pthread_flags" ;; esac # Clang doesn't consider unrecognized options an error unless we specify # -Werror. We throw in some extra Clang-specific options to ensure that # this doesn't happen for GCC, which also accepts -Werror. AC_MSG_CHECKING([if compiler needs -Werror to reject unknown flags]) save_CFLAGS="$CFLAGS" ax_pthread_extra_flags="-Werror" CFLAGS="$CFLAGS $ax_pthread_extra_flags -Wunknown-warning-option -Wsizeof-array-argument" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([int foo(void);],[foo()])], [AC_MSG_RESULT([yes])], [ax_pthread_extra_flags= AC_MSG_RESULT([no])]) CFLAGS="$save_CFLAGS" if test x"$ax_pthread_ok" = xno; then for flag in $ax_pthread_flags; do case $flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -*) AC_MSG_CHECKING([whether pthreads work with $flag]) PTHREAD_CFLAGS="$flag" ;; pthread-config) AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) if test x"$ax_pthread_config" = xno; then continue; fi PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) AC_MSG_CHECKING([for the pthreads library -l$flag]) PTHREAD_LIBS="-l$flag" ;; esac save_LIBS="$LIBS" save_CFLAGS="$CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS $ax_pthread_extra_flags" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. AC_LINK_IFELSE([AC_LANG_PROGRAM([#include static void routine(void *a) { a = 0; } static void *start_routine(void *a) { return a; }], [pthread_t th; pthread_attr_t attr; pthread_create(&th, 0, start_routine, 0); pthread_join(th, 0); pthread_attr_init(&attr); pthread_cleanup_push(routine, 0); pthread_cleanup_pop(0) /* ; */])], [ax_pthread_ok=yes], []) LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" AC_MSG_RESULT([$ax_pthread_ok]) if test "x$ax_pthread_ok" = xyes; then break; fi PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$ax_pthread_ok" = xyes; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_MSG_CHECKING([for joinable pthread attribute]) attr_name=unknown for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], [int attr = $attr; return attr /* ; */])], [attr_name=$attr; break], []) done AC_MSG_RESULT([$attr_name]) if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$attr_name], [Define to necessary symbol if this constant uses a non-standard name on your system.]) fi AC_MSG_CHECKING([if more special flags are required for pthreads]) flag=no case ${host_os} in aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";; osf* | hpux*) flag="-D_REENTRANT";; solaris*) if test "$GCC" = "yes"; then flag="-D_REENTRANT" else # TODO: What about Clang on Solaris? flag="-mt -D_REENTRANT" fi ;; esac AC_MSG_RESULT([$flag]) if test "x$flag" != xno; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], [ax_cv_PTHREAD_PRIO_INHERIT], [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[int i = PTHREAD_PRIO_INHERIT;]])], [ax_cv_PTHREAD_PRIO_INHERIT=yes], [ax_cv_PTHREAD_PRIO_INHERIT=no]) ]) AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"], [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])]) LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" # More AIX lossage: compile with *_r variant if test "x$GCC" != xyes; then case $host_os in aix*) AS_CASE(["x/$CC"], [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], [#handle absolute path differently from PATH based program lookup AS_CASE(["x$CC"], [x/*], [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) ;; esac fi fi test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" AC_SUBST([PTHREAD_LIBS]) AC_SUBST([PTHREAD_CFLAGS]) AC_SUBST([PTHREAD_CC]) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test x"$ax_pthread_ok" = xyes; then ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) : else ax_pthread_ok=no $2 fi AC_LANG_POP ])dnl AX_PTHREAD wimlib-1.13.1/m4/ltoptions.m40000644000175000017500000003426213427652374012642 00000000000000# Helper functions for option handling. -*- Autoconf -*- # # Copyright (C) 2004-2005, 2007-2009, 2011-2018 Free Software # Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 8 ltoptions.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) # _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) # ------------------------------------------ m4_define([_LT_MANGLE_OPTION], [[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) # _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) # --------------------------------------- # Set option OPTION-NAME for macro MACRO-NAME, and if there is a # matching handler defined, dispatch to it. Other OPTION-NAMEs are # saved as a flag. m4_define([_LT_SET_OPTION], [m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), _LT_MANGLE_DEFUN([$1], [$2]), [m4_warning([Unknown $1 option '$2'])])[]dnl ]) # _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) # ------------------------------------------------------------ # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. m4_define([_LT_IF_OPTION], [m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) # _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) # ------------------------------------------------------- # Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME # are set. m4_define([_LT_UNLESS_OPTIONS], [m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), [m4_define([$0_found])])])[]dnl m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 ])[]dnl ]) # _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) # ---------------------------------------- # OPTION-LIST is a space-separated list of Libtool options associated # with MACRO-NAME. If any OPTION has a matching handler declared with # LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about # the unknown option and exit. m4_defun([_LT_SET_OPTIONS], [# Set options m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [_LT_SET_OPTION([$1], _LT_Option)]) m4_if([$1],[LT_INIT],[ dnl dnl Simply set some default values (i.e off) if boolean options were not dnl specified: _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no ]) _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no ]) dnl dnl If no reference was made to various pairs of opposing options, then dnl we run the default mode handler for the pair. For example, if neither dnl 'shared' nor 'disable-shared' was passed, we enable building of shared dnl archives by default: _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], [_LT_ENABLE_FAST_INSTALL]) _LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4], [_LT_WITH_AIX_SONAME([aix])]) ]) ])# _LT_SET_OPTIONS ## --------------------------------- ## ## Macros to handle LT_INIT options. ## ## --------------------------------- ## # _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) # ----------------------------------------- m4_define([_LT_MANGLE_DEFUN], [[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) # LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) # ----------------------------------------------- m4_define([LT_OPTION_DEFINE], [m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl ])# LT_OPTION_DEFINE # dlopen # ------ LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes ]) AU_DEFUN([AC_LIBTOOL_DLOPEN], [_LT_SET_OPTION([LT_INIT], [dlopen]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'dlopen' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) # win32-dll # --------- # Declare package support for building win32 dll's. LT_OPTION_DEFINE([LT_INIT], [win32-dll], [enable_win32_dll=yes case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*) AC_CHECK_TOOL(AS, as, false) AC_CHECK_TOOL(DLLTOOL, dlltool, false) AC_CHECK_TOOL(OBJDUMP, objdump, false) ;; esac test -z "$AS" && AS=as _LT_DECL([], [AS], [1], [Assembler program])dnl test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl ])# win32-dll AU_DEFUN([AC_LIBTOOL_WIN32_DLL], [AC_REQUIRE([AC_CANONICAL_HOST])dnl _LT_SET_OPTION([LT_INIT], [win32-dll]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'win32-dll' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) # _LT_ENABLE_SHARED([DEFAULT]) # ---------------------------- # implement the --enable-shared flag, and supports the 'shared' and # 'disable-shared' LT_INIT options. # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_SHARED], [m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([shared], [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS=$lt_save_ifs ;; esac], [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) _LT_DECL([build_libtool_libs], [enable_shared], [0], [Whether or not to build shared libraries]) ])# _LT_ENABLE_SHARED LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) # Old names: AC_DEFUN([AC_ENABLE_SHARED], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) ]) AC_DEFUN([AC_DISABLE_SHARED], [_LT_SET_OPTION([LT_INIT], [disable-shared]) ]) AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_SHARED], []) dnl AC_DEFUN([AM_DISABLE_SHARED], []) # _LT_ENABLE_STATIC([DEFAULT]) # ---------------------------- # implement the --enable-static flag, and support the 'static' and # 'disable-static' LT_INIT options. # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_STATIC], [m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([static], [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS=$lt_save_ifs ;; esac], [enable_static=]_LT_ENABLE_STATIC_DEFAULT) _LT_DECL([build_old_libs], [enable_static], [0], [Whether or not to build static libraries]) ])# _LT_ENABLE_STATIC LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) # Old names: AC_DEFUN([AC_ENABLE_STATIC], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) ]) AC_DEFUN([AC_DISABLE_STATIC], [_LT_SET_OPTION([LT_INIT], [disable-static]) ]) AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_STATIC], []) dnl AC_DEFUN([AM_DISABLE_STATIC], []) # _LT_ENABLE_FAST_INSTALL([DEFAULT]) # ---------------------------------- # implement the --enable-fast-install flag, and support the 'fast-install' # and 'disable-fast-install' LT_INIT options. # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_FAST_INSTALL], [m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([fast-install], [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS=$lt_save_ifs ;; esac], [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) _LT_DECL([fast_install], [enable_fast_install], [0], [Whether or not to optimize for fast installation])dnl ])# _LT_ENABLE_FAST_INSTALL LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) # Old names: AU_DEFUN([AC_ENABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'fast-install' option into LT_INIT's first parameter.]) ]) AU_DEFUN([AC_DISABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], [disable-fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'disable-fast-install' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) # _LT_WITH_AIX_SONAME([DEFAULT]) # ---------------------------------- # implement the --with-aix-soname flag, and support the `aix-soname=aix' # and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT # is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'. m4_define([_LT_WITH_AIX_SONAME], [m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl shared_archive_member_spec= case $host,$enable_shared in power*-*-aix[[5-9]]*,yes) AC_MSG_CHECKING([which variant of shared library versioning to provide]) AC_ARG_WITH([aix-soname], [AS_HELP_STRING([--with-aix-soname=aix|svr4|both], [shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])], [case $withval in aix|svr4|both) ;; *) AC_MSG_ERROR([Unknown argument to --with-aix-soname]) ;; esac lt_cv_with_aix_soname=$with_aix_soname], [AC_CACHE_VAL([lt_cv_with_aix_soname], [lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT) with_aix_soname=$lt_cv_with_aix_soname]) AC_MSG_RESULT([$with_aix_soname]) if test aix != "$with_aix_soname"; then # For the AIX way of multilib, we name the shared archive member # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, # the AIX toolchain works better with OBJECT_MODE set (default 32). if test 64 = "${OBJECT_MODE-32}"; then shared_archive_member_spec=shr_64 else shared_archive_member_spec=shr fi fi ;; *) with_aix_soname=aix ;; esac _LT_DECL([], [shared_archive_member_spec], [0], [Shared archive member basename, for filename based shared library versioning on AIX])dnl ])# _LT_WITH_AIX_SONAME LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])]) LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])]) LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])]) # _LT_WITH_PIC([MODE]) # -------------------- # implement the --with-pic flag, and support the 'pic-only' and 'no-pic' # LT_INIT options. # MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'. m4_define([_LT_WITH_PIC], [AC_ARG_WITH([pic], [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@], [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], [lt_p=${PACKAGE-default} case $withval in yes|no) pic_mode=$withval ;; *) pic_mode=default # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for lt_pkg in $withval; do IFS=$lt_save_ifs if test "X$lt_pkg" = "X$lt_p"; then pic_mode=yes fi done IFS=$lt_save_ifs ;; esac], [pic_mode=m4_default([$1], [default])]) _LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl ])# _LT_WITH_PIC LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) # Old name: AU_DEFUN([AC_LIBTOOL_PICMODE], [_LT_SET_OPTION([LT_INIT], [pic-only]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'pic-only' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) ## ----------------- ## ## LTDL_INIT Options ## ## ----------------- ## m4_define([_LTDL_MODE], []) LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], [m4_define([_LTDL_MODE], [nonrecursive])]) LT_OPTION_DEFINE([LTDL_INIT], [recursive], [m4_define([_LTDL_MODE], [recursive])]) LT_OPTION_DEFINE([LTDL_INIT], [subproject], [m4_define([_LTDL_MODE], [subproject])]) m4_define([_LTDL_TYPE], []) LT_OPTION_DEFINE([LTDL_INIT], [installable], [m4_define([_LTDL_TYPE], [installable])]) LT_OPTION_DEFINE([LTDL_INIT], [convenience], [m4_define([_LTDL_TYPE], [convenience])]) wimlib-1.13.1/COPYING0000644000175000017500000000337313160354224011042 00000000000000Unless otherwise specified, wimlib and its associated programs, scripts, documentation, and other files may be redistributed and/or modified under the terms of the GNU General Public License; either version 3 of the License, or (at your option) any later version. There is NO WARRANTY, to the extent permitted by law. See the file COPYING.GPLv3 for more details. Alternatively, when not prohibited by conflict with a third-party software license, the library portion of wimlib may be redistributed and/or modified under the terms of the GNU Lesser General Public License; either version 3 of the License, or (at your option) any later version. There is NO WARRANTY, to the extent permitted by law. See the file COPYING.LGPLv3 for more details. ---------------------------------------- NOTE! The primary reason for the GPL/LGPL "dual licensing" for the library is that on UNIX-like systems, wimlib can optionally be linked to the third-party library "libntfs-3g", which is licensed GPLv2+. Under some interpretations of the GPL, this would require that wimlib be licensed under the GPL as well. However, a binary copy of wimlib that was compiled without libntfs-3g support (for example; the exception may be applicable in other situations as well) logically cannot be affected by libntfs-3g's license and should therefore be free to be redistributed under the LGPL instead of the GPL. NOTE! The file COPYING.CC0 contains a public domain dedication. This public domain dedication does not apply to wimlib as a whole, but rather to individual source code files which the author(s) have elected to place into the public domain, as noted in the corresponding file headers. As usual, such code carries NO WARRANTY, to the extent permitted by law. See COPYING.CC0 for more details. wimlib-1.13.1/doc/0000755000175000017500000000000013464166632010641 500000000000000wimlib-1.13.1/doc/man1/0000755000175000017500000000000013464166632011475 500000000000000wimlib-1.13.1/doc/man1/wimverify.10000644000175000017500000000375013464166215013522 00000000000000.TH WIMVERIFY "1" "May 2019" "wimlib 1.13.1" "User Commands" .SH NAME wimverify \- Verify a WIM archive .SH SYNOPSIS \fBwimverify\fR \fIWIMFILE\fR [\fIOPTION\fR...] .SH DESCRIPTION \fBwimverify\fR (or equivalently \fBwimlib-imagex verify\fR) checks the validity and integrity of the specified WIM archive. .PP Specifically, this command performs the following verifications on the WIM: .IP \[bu] 4 Verify that the WIM can be successfully opened, which involves parsing the header, blob table, and XML data. .IP \[bu] If the WIM contains extra integrity information, verify the integrity of the entire WIM. .IP \[bu] Verify that the metadata for each image in the WIM can be successfully parsed. .IP \[bu] Verify that all files needed by each image are actually contained in the WIM or in one of the WIMs referenced by the \fB--ref\fR option. .IP \[bu] Verify that all files contained in the WIM can be successfully decompressed, with matching checksums. .SH OPTIONS .TP 6 \fB--ref\fR="\fIGLOB\fR" File glob of additional WIMs or split WIM parts to reference resources from. This option can be specified multiple times. Note: \fIGLOB\fR is listed in quotes because it is interpreted by \fBwimverify\fR and may need to be quoted to protect against shell expansion. .TP \fB--nocheck\fR Do not verify the WIM's integrity using the extra integrity information (the integrity table). .SH NOTES \fBwimverify\fR is a read-only operation; it does not modify the WIM file. .PP Even if the WIM does not contain extra integrity information (e.g. generated with the \fB--check\fR option to \fBwimcapture\fR), \fBwimverify\fR may still be quite useful because all file data is still checksummed. .PP In the future, \fBwimverify\fR might do more thorough verifications than it does now. .SH EXAMPLES Verify the WIM file 'boot.wim': .RS .PP wimverify boot.wim .RE .PP Verify the split WIM file consisting of 'boot.swm', 'boot2.swm', 'boot3.swm', ...: .RS .PP wimverify boot.swm --ref="boot*.swm" .RE .PP .SH SEE ALSO .BR wimlib-imagex (1) wimlib-1.13.1/doc/man1/wiminfo.10000644000175000017500000000525013464166215013146 00000000000000.TH WIMINFO "1" "May 2019" "wimlib 1.13.1" "User Commands" .SH NAME wiminfo \- Display or change information about a WIM file or image .SH SYNOPSIS \fBwiminfo\fR \fIWIMFILE\fR [\fIIMAGE\fR [\fINEW_NAME\fR [\fINEW_DESC\fR]]] [\fIOPTION\fR...] .SH DESCRIPTION \fBwiminfo\fR, or equivalently \fBwimlib-imagex info\fR, displays information about \fIWIMFILE\fR or the specified \fIIMAGE\fR in it, and optionally changes properties of \fIIMAGE\fR such as its name and description, or changes the bootable image of the WIM. .PP If neither an image nor any flags other than \fB--check\fR are specified, then basic information about the WIM and the images contained in it is shown. If an image is specified by \fIIMAGE\fR (as a 1-based image index or an image name), then the information is restricted to that concerning the specified image. .PP Changes to the WIM are made if \fINEW_NAME\fR and/or \fB--boot\fR and/or \fB--image-property\fR are specified. \fINEW_NAME\fR is taken to be the new name of the image specified by \fIIMAGE\fR while \fINEW_DESC\fR is taken to be its new description. If \fINEW_DESC\fR is not specified, then the image's description is not changed. .PP \fBwiminfo\fR does not support modifying a split WIM, although you may display information about one, including any of its parts. .SH OPTIONS .TP 6 \fB--boot\fR Mark the specified \fIIMAGE\fR as the "bootable" image of the WIM. The "bootable" image is the image which the Windows bootloader will use when loading Windows PE from the WIM. .TP \fB--check\fR Verify the integrity of WIM if it contains extra integrity information. In addition, if modifying the WIM, add extra integrity information if it was not present before. .TP \fB--nocheck\fR If modifying the WIM, remove its extra integrity information, if it had any. .TP \fB--include-integrity\fR If modifying the WIM, add extra integrity information if it was not present before, i.e. like \fB--check\fR but don't also do the verification beforehand. .TP \fB--image-property\fR \fINAME\fR=\fIVALUE\fR Assign an arbitrary property to the specified \fIIMAGE\fR in the XML document of the WIM. \fINAME\fR is an element path such as "WINDOWS/VERSION/MAJOR", and \fIVALUE\fR is the string to place in the element, such as "10". See the documentation for this option to \fBwimcapture\fR(1) for more details. This option may be specified multiple times. .TP \fB--header\fR Show detailed information from the WIM header. .TP \fB--blobs\fR List all the "blobs" (unique file data) in the WIM. .TP \fB--extract-xml\fR=\fIFILE\fR Extract the WIM's raw XML document to \fIFILE\fR. .TP \fB--xml\fR .br Extract the WIM's raw XML document to standard output. .SH SEE ALSO .BR wimlib-imagex (1) .BR wimdir (1) wimlib-1.13.1/doc/man1/wimdelete.10000644000175000017500000000465213464166215013462 00000000000000.TH WIMDELETE "1" "May 2019" "wimlib 1.13.1" "User Commands" .SH NAME wimdelete \- Delete an image from a WIM archive .SH SYNOPSIS \fBwimdelete\fR \fIWIMFILE\fR \fIIMAGE\fR [\fIOPTION\fR...] .SH DESCRIPTION \fBwimdelete\fR, or equivalently \fBwimlib-imagex delete\fR, deletes the specified image from the Windows Imaging (WIM) archive \fIWIMFILE\fR. .PP \fIIMAGE\fR specifies the WIM image in \fIWIMFILE\fR to delete. It may be the 1-based index of an image, the name of an image, or the keyword "all" to specify all images. You can use \fBwiminfo\fR(1) to list the images contained in \fIWIMFILE\fR. .SH NOTES By default, \fBwimdelete\fR rebuilds the WIM with all unnecessary file data removed. This is different from Microsoft's ImageX and DISM, which only will delete the directory tree metadata and XML data for this operation. Use \fB--soft\fR if you want the other kind of delete. .PP wimlib allows you to delete all the images from a WIM and have a WIM with 0 images, although such a file may not be very useful. .PP \fBwimdelete\fR does not support split WIMs. .SH OPTIONS .TP 6 \fB--check\fR Before deleting the image, verify the WIM's integrity if extra integrity information is present. In addition, include extra integrity information in the modified WIM, even if it was not present before. .TP \fB--include-integrity\fR Include extra integrity information in the modified WIM, i.e. like \fB--check\fR but don't do any verification beforehand. .TP \fB--soft\fR Perform a "soft delete". Specifying this flag overrides the default behavior of rebuilding the entire WIM after deleting an image. Instead, only minimal changes to correctly remove the image from the WIM will be taken. In particular, all file resources will be left alone, even if they are no longer referenced. This may not be what you want, because no space will be saved by deleting an image in this way. However, \fBwimoptimize\fR can later be used to rebuild a WIM file that has had images soft-deleted from it. .TP \fB--unsafe-compact\fR Compact the WIM archive in-place, eliminating "holes". This is efficient, but in general this option should \fInot\fR be used because a failed or interrupted compaction will corrupt the WIM archive. For more information, see the documentation for this option to \fBwimoptimize\fR(1). .SH EXAMPLES Delete the first image from 'boot.wim': .RS .PP wimdelete boot.wim 1 .RE .PP .SH SEE ALSO .BR wimlib-imagex (1) .BR wiminfo (1) .BR wimoptimize (1) wimlib-1.13.1/doc/man1/wimupdate.10000644000175000017500000002566513464166215013511 00000000000000.TH WIMUPDATE "1" "May 2019" "wimlib 1.13.1" "User Commands" .SH NAME wimupdate \- Update a WIM image .SH SYNOPSIS \fBwimupdate\fR \fIWIMFILE\fR [\fIIMAGE\fR] [\fIOPTION\fR...] [< \fICMDFILE\fR] .SH DESCRIPTION \fBwimupdate\fR, or equivalently \fBwimlib-imagex update\fR, modifies the specified \fIIMAGE\fR in the Windows Imaging (WIM) archive \fIWIMFILE\fR by adding, deleting, or renaming files or directories in it. .PP \fIIMAGE\fR specifies the image in \fIWIMFILE\fR to update. It may be the 1-based index of an image or the name of an image. It may be omitted if \fIWIMFILE\fR contains only one image. You can use \fBwiminfo\fR(1) to list the images contained in \fIWIMFILE\fR. .PP The modifications to perform on the WIM image are specified as a sequence of commands, one per line, read in a text file from standard input. It is recommended that standard input be redirected from a file (\fICMDFILE\fR), as shown above, rather than typing in commands interactively. Alternatively, to specify a command directly on the command line, see the \fB--command\fR option. .SH AVAILABLE COMMANDS This section documents the commands that may appear in the \fICMDFILE\fR described above. .SS \fBadd\fR [\fIOPTION\fR...] \fISOURCE\fR \fIDESTINATION\fR Add a file or directory tree to the WIM image. \fISOURCE\fR must specify the path to a file or directory on your filesystem. \fIDESTINATION\fR must specify the path inside the WIM image at which to add the file or directory tree. .PP If \fIDESTINATION\fR names an existing directory in the WIM image, then \fISOURCE\fR must also name a directory. This causes the contents of the \fISOURCE\fR directory to be added to the \fIDESTINATION\fR directory. .PP If \fIDESTINATION\fR names an existing nondirectory file in the WIM image, then \fISOURCE\fR must also name a nondirectory file. By default, this causes the \fIDESTINATION\fR file to be replaced with the \fISOURCE\fR file. Or, with \fB--no-replace\fR specified, this generates an error. .PP If \fIDESTINATION\fR does not exist in the WIM image, then any prerequisite directories are created as needed to add the \fISOURCE\fR at that location. .PP The \fBadd\fR command supports a subset of the options accepted by \fBwimcapture\fR; namely, \fB--dereference\fR, \fB--unix-data\fR, \fB--no-acls\fR, and \fB--strict-acls\fR. See \fBwimcapture\fR(1) for explanations of these options. .PP In addition, the \fBadd\fR command supports the \fB--no-replace\fR option, which causes the \fBadd\fR command to refuse to overwrite existing nondirectory files in the WIM image. .SS \fBdelete\fR [\fIOPTION\fR...] \fIPATH\fR Delete a file or directory tree from the WIM image. \fIPATH\fR must specify the path inside the WIM image of the file or directory tree to delete. .PP The available options for the \fBdelete\fR command are: .TP 6 \fB--force\fR Do not issue an error if the path to delete does not exist. .TP \fB--recursive\fR Delete the file or directory tree recursively; if not specified, an error is issued if the path to delete is a directory. .SS \fBrename\fR \fIOLD_PATH\fR \fINEW_PATH\fR Rename a file or directory tree inside the WIM image. \fIOLD_PATH\fR must specify the old path of the file or directory tree inside the WIM image, and \fINEW_PATH\fR must specify the new path for the file or directory tree. This command follows the semantics of the POSIX \fBrename\fR (3) function; in particular, a pre-existing file at \fINEW_PATH\fR will be deleted if present, except in certain cases such as attempting to rename a directory to a non-directory, which is not allowed. .PP There are no options available for the \fBrename\fR command. .SH OPTIONS The following options are accepted on the command line by \fBwimupdate\fR itself: .TP 6 \fB--dereference\fR Use \fB--dereference\fR for all \fBadd\fR commands. .TP \fB--unix-data\fR Use \fB--unix-data\fR for all \fBadd\fR commands. .TP \fB--no-acls\fR Use \fB--no-acls\fR for all \fBadd\fR commands. .TP \fB--strict-acls\fR Use \fB--strict-acls\fR for all \fBadd\fR commands. .TP \fB--no-replace\fR Use \fB--no-replace\fR for all \fBadd\fR commands. .TP \fB--config\fR=\fIFILE\fR Set the capture configuration file for all \fBadd\fR commands. See the description of this option to \fBwimcapture\fR(1). .TP \fB--force\fR Use \fB--force\fR for all \fBdelete\fR commands. .TP \fB--recursive\fR Use \fB--recursive\fR for all \fBdelete\fR commands. .TP \fB--check\fR Before updating the WIM, verify its integrity if it contains extra integrity information. Also include extra integrity information in the updated WIM even if it was not present before. .TP \fB--include-integrity\fR Include extra integrity information in the updated WIM, i.e. like \fB--check\fR but don't also verify the WIM beforehand. .TP \fB--threads\fR=\fINUM_THREADS\fR Number of threads to use for compressing newly added files. Default: autodetect (number of processors). .TP \fB--rebuild\fR Rebuild the entire WIM rather than appending the updated data to the end of it. Rebuilding the WIM is slower, but will save a little bit of space that would otherwise be left as a hole in the WIM file. .IP See \fBwimoptimize\fR(1) for a more customizable way of rebuilding (and optionally recompressing) a WIM file. If running \fBwimoptimize\fR after \fBwimupdate\fR, there is no need to specify \fB--rebuild\fR to \fBwimupdate\fR. .TP \fB--command\fR=\fISTRING\fR Instead of reading update commands from standard input, read a single update command directly from the string \fISTRING\fR specified on the command line. This option cannot be provided more than one time and cannot be used to specify more than one update command. Note that the \fISTRING\fR, as well as any paths containing spaces within the \fISTRING\fR must be appropriately quoted. If running from cmd.exe on Windows, you should use double quotes for the outer quotes and single quotes for the inner quotes. Example: .IP .RS .RS wimupdate boot.wim 1 --command="add 'C:\\My Dir' '\\My Dir'" .RE .RE .IP This option is provided for convenience only. Do not execute \fBwimupdate\fR multiple consecutive times, each time passing the \fB--command\fR option! This is inefficient. Instead, generate an update command file and provide it (on standard input) to a single invocation of \fBwimupdate\fR, as explained in this document. .TP \fB--wimboot-config\fR=\fIFILE\fR If this option is specified, no commands shall be read from standard input, and instead the following command shall be executed: .IP .nf .RS .RS \fBadd\fR \fIFILE\fR /Windows/System32/WimBootCompress.ini .RE .RE .fi .IP This sets \fIFILE\fR as the WIMBoot configuration file for the image. The [PrepopulateList] section of this file specifies path globs that shall not be extracted as WIMBoot pointer files (perhaps due to being needed early in the boot process). See the documentation for the \fB--wimboot\fR option of \fBwimapply\fR(1) for more information. .TP \fB--unsafe-compact\fR Compact the WIM archive in-place and append any new data, eliminating "holes". This is efficient, but in general this option should \fInot\fR be used because a failed or interrupted compaction will corrupt the WIM archive. For more information, see the documentation for this option in \fBwimoptimize\fR(1). .SH NOTES \fBwimupdate\fR can be viewed as redundant with \fBwimmountrw\fR, since a WIM image can also be updated by mounting it read-write. However, \fBwimupdate\fR works on all platforms including Windows, whereas \fBwimmountrw\fR only works on Linux. .PP Symbolic links inside a WIM image are not dereferenced when being interpreted. So, for example, if you have a WIM image that contains a symbolic link "/Documents and Settings" -> "/Users" where "/Users" is a directory, then a subdirectory named "Public" in this directory must be specified as "/Users/Public" rather than "/Documents and Settings/Public". .PP All paths to files or directories within the WIM image must be specified relative to the root of the image. However, the leading slash is optional, and both forward slashes and backslashes are accepted. In addition, on Windows, the paths are by default treated case-insensitively, while on UNIX-like systems, the paths are by default treated case-sensitively. The default case sensitivity may be changed by setting the \fBWIMLIB_IMAGEX_IGNORE_CASE\fR environmental variable to 0 or 1. .PP The command file (\fICMDFILE\fR) is parsed by \fBwimupdate\fR itself and not by the system shell. Therefore, its syntax is limited. However, comment lines beginning with '#' are allowed, and it is also possible to quote arguments with whitespace inside them. .PP On UNIX-like systems, you cannot use \fBwimupdate\fR to add files to an image directly from an NTFS volume using libntfs-3g, even though \fBwimcapture\fR supports capturing a full image this way. .PP Except when using \fB--unsafe-compact\fR, it is safe to abort a \fBwimupdate\fR command partway through; however, after doing this, it is recommended to run \fBwimoptimize\fR to remove any data that was appended to the physical WIM file but not yet incorporated into the structure of the WIM, unless \fB--rebuild\fR was specified, in which case you should delete the temporary file left over. .SH EXAMPLES All the examples below show the update command file to be created as well as the \fBwimupdate\fR command to run to perform the updates. .PP Delete two files from a WIM image: .PP .RS \fIupdate_commands.txt\fR: .RS .PP .nf delete /setup.exe delete /sources/setup.exe .fi .RE .RE .PP .RS $ wimupdate boot.wim 2 < update_commands.txt .RE .PP Add some files and directories to a WIM image. Note that the first path of each \fBadd\fR command specifies the files to add, while the second path of each \fBadd\fR command specify the locations at which to to add them inside the WIM image: .PP .RS \fIupdate_commands.txt\fR: .RS .PP .nf add somedir /dir add somefile /dir/file .fi .RE .RE .PP .RS $ wimupdate boot.wim 2 < update_commands.txt .RE .PP Rename a file inside a WIM image. .PP .RS \fIupdate_commands.txt\fR: .RS .PP .nf rename /dir_in_wim/oldfile.txt /dir_in_wim/newfile.txt .fi .RE .RE .PP .RS $ wimupdate boot.wim 2 < update_commands.txt .RE .PP Using additional features, such as comments, options, and overlays, and including extra integrity information in the updated WIM: .PP .RS \fIupdate_commands.txt\fR: .RS .PP .nf # # This file specifies some changes to make to a WIM image. # # Add a new directory containing files I want in the image. # The quotes are necessary because the directory name # contains a space. add "My Directory" "/My Directory" # Add the contents of "Another Directory" to the # "/My Directory" we just created in the WIM image. Since # the destination path already exists, this performs an # overlay. add "Another Directory" "/My Directory" # Rename some file for some reason. rename /dir_in_wim/oldfile.txt /dir_in_wim/newfile.txt # Delete an unwanted directory. delete --recursive /Users/Me/Documents/Junk .fi .RE .RE .PP .RS $ wimupdate boot.wim 2 --check < update_commands.txt .RE .PP .SH SEE ALSO .BR wimlib-imagex (1) .BR wimcapture (1) .BR wiminfo (1) .BR wimmountrw (1) .BR wimoptimize (1) wimlib-1.13.1/doc/man1/wimdir.10000644000175000017500000000307113464166215012770 00000000000000.TH WIMDIR "1" "May 2019" "wimlib 1.13.1" "User Commands" .SH NAME wimdir \- List the files contained in a WIM image .SH SYNOPSIS \fBwimdir\fR \fIWIMFILE\fR [\fIIMAGE\fR] [\fIOPTION\fR...] .SH DESCRIPTION \fBwimdir\fR, or equivalently \fBwimlib-imagex dir\fR, lists the files and directories contained in the specified image of the Windows Imaging (WIM) archive \fIWIMFILE\fR. .PP \fIIMAGE\fR specifies the image in \fIWIMFILE\fR to list. It may be the 1-based index of an image, the name of an image, or the keyword "all" to specify all images. It may be omitted if \fBWIMFILE\fR contains only one image. You can use \fBwiminfo\fR(1) to list the images contained in \fIWIMFILE\fR. .SH OPTIONS .TP 6 \fB--path\fR=\fIPATH\fR List the files under \fIPATH\fR instead of under the root directory. .TP \fB--detailed\fR List detailed information about each file. .TP \fB--one-file-only\fR List information about the specified file only. Intended for use with both \fB--path\fR and \fB--detailed\fR. .TP \fB--ref\fR="\fIGLOB\fR" File glob of additional WIMs or split WIM parts to reference resources from. This option can be specified multiple times. This option is only useful when \fB--detailed\fR is also specified. .SH NOTES \fBwimdir\fR supports split WIMs, but it only works on the first part of the split WIM. .PP Detailed metadata such as timestamps and data streams is not shown unless the \fB--detailed\fR option is used. .SH EXAMPLES List all files and directories in the first image of 'boot.wim': .RS .PP wimdir boot.wim 1 .RE .PP .SH SEE ALSO .BR wimlib-imagex (1) .BR wiminfo (1) wimlib-1.13.1/doc/man1/wimcapture.10000644000175000017500000010071513464166215013660 00000000000000.TH WIMCAPTURE "1" "May 2019" "wimlib 1.13.1" "User Commands" .SH NAME wimcapture, wimappend \- Capture or append a WIM image .SH SYNOPSIS \fBwimcapture\fR \fISOURCE\fR \fIWIMFILE\fR [\fIIMAGE_NAME\fR \ [\fIIMAGE_DESC\fR]] [\fIOPTION\fR...] .br \fBwimappend\fR \fISOURCE\fR \fIWIMFILE\fR [\fIIMAGE_NAME\fR \ [\fIIMAGE_DESC\fR]] [\fIOPTION\fR...] .SH DESCRIPTION The \fBwimcapture\fR (equivalently: \fBwimlib-imagex capture\fR) and \fBwimappend\fR (equivalently: \fBwimlib-imagex append\fR) commands create ("capture") a new Windows Imaging (WIM) image. \fBwimcapture\fR creates a new WIM archive \fIWIMFILE\fR to contain the new image, while \fBwimappend\fR adds the image to the existing WIM archive \fIWIMFILE\fR (or with \fB--create\fR, creating it if needed). .PP \fISOURCE\fR specifies the location of the files from which to create the WIM image. If \fISOURCE\fR is a directory or a symbolic link pointing to a directory, then the image is captured from that directory as per \fBDIRECTORY CAPTURE (UNIX)\fR or \fBDIRECTORY CAPTURE (WINDOWS)\fR. Alternatively, if \fB--source-list\fR is specified, then \fISOURCE\fR is interpreted as a file containing a list of files and directories to include in the image. Still alternatively, if \fISOURCE\fR is a UNIX block device, then an image is captured from the NTFS volume on it as per \fBNTFS VOLUME CAPTURE (UNIX)\fR. .PP \fIIMAGE_NAME\fR and \fIIMAGE_DESC\fR specify the name and description to give the new image. If \fIIMAGE_NAME\fR is unspecified, it defaults to the filename component of \fISOURCE\fR, appending a unique suffix if needed. Otherwise, \fIIMAGE_NAME\fR must be either a name not already used by an image in \fIWIMFILE\fR, or the empty string to create an unnamed image. If \fIIMAGE_DESC\fR is unspecified then the new image is given no description. .PP If \fIWIMFILE\fR is specified as "-", then the \fB--pipable\fR option is assumed and a pipable WIM is written to standard output (this is a wimlib extension). .SH DIRECTORY CAPTURE (UNIX) On UNIX-like systems, if \fISOURCE\fR specifies a directory or a symbolic link to a directory, then the WIM image will be captured from that directory. The directory can be on any type of filesystem, and mountpoints are followed. In this mode, the following types of information are captured: .IP \[bu] 4 Directories and regular files, and the contents of regular files .IP \[bu] Hard links .IP \[bu] Symbolic links (translated losslessly to Windows reparse points) .IP \[bu] Last modification times (mtime) and last access times (atime) with 100 nanosecond granularity .IP \[bu] Files that appear to be sparse will be flagged as such, but their full data will still be stored, subject to the usual compression. .IP \[bu] With \fB--unix-data\fR: standard UNIX file permissions (owner, group, and mode) .IP \[bu] With \fB--unix-data\fR: device nodes, named pipes, and sockets .IP \[bu] With \fB--unix-data\fR: extended attributes (Linux only) .PP There is no support for storing last status change times (ctimes), or hard link information for symlinks (each symlink will be stored as a separate file). Also, filenames and symlink targets which are not valid UTF-8 with the addition of surrogate codepoints are unsupported. Note that if you have a filesystem containing filenames in another multibyte encoding, such as ISO-8859-1, and you wish to archive it with wimlib, you may be able to mount it with an option which causes its filenames to be presented as UTF-8. .SH NTFS VOLUME CAPTURE (UNIX) On UNIX-like systems, \fISOURCE\fR may also be specified as a block device (e.g. /dev/sda3) containing an unmounted NTFS volume. In this mode, \fBwimcapture\fR uses libntfs-3g to capture a WIM image from root directory of the NTFS volume. In this mode, as much data and metadata as possible is captured, including NTFS-specific and Windows-specific metadata: .IP \[bu] 4 All data streams of all unencrypted files, including the unnamed data stream as well as all named data streams. .IP \[bu] Reparse points. See \fBREPARSE POINTS AND SYMLINKS\fR for details. .IP \[bu] File and directory creation, access, and modification timestamps, using the native NTFS resolution of 100 nanoseconds. .IP \[bu] Windows security descriptors, including all components (owner, group, DACL, and SACL). .IP \[bu] DOS/Windows file attribute flags. .IP \[bu] All names of all files, including names in the Win32 namespace, DOS namespace, Win32+DOS namespace, and POSIX namespace. This includes hard links. .IP \[bu] Object IDs. .PP However, the main limitations of this mode are: .IP \[bu] 4 Encrypted files are excluded. .IP \[bu] Extended attributes (EAs) are not stored yet. .IP \[bu] Sparse files will be flagged as such, but their full data will still be stored, subject to the usual compression. .IP \[bu] Some types of reparse points are transparently dereferenced by Windows but not by NTFS-3G. See \fBREPARSE POINTS AND SYMLINKS\fR. .PP Note that this mode uses libntfs-3g directly, without going through the \fBntfs-3g\fR(8) driver. Hence, there is no special support for capturing a WIM image from a directory on which an NTFS filesystem has been mounted using \fBntfs-3g\fR(8); you have to unmount it first. There is also no support for capturing a subdirectory of the NTFS volume; you can only capture the full volume. .SH DIRECTORY CAPTURE (WINDOWS) On Windows, \fBwimcapture\fR and \fBwimappend\fR natively support Windows-specific and NTFS-specific data. They therefore act similarly to the corresponding commands of Microsoft's ImageX or DISM. For best results, the directory being captured should be on an NTFS volume and the program should be run with Administrator privileges; however, non-NTFS filesystems and running without Administrator privileges are also supported, subject to limitations. .PP On Windows, \fBwimcapture\fR and \fBwimappend\fR try to capture as much data and metadata as possible, including: .IP \[bu] 4 All data streams of all files. .IP \[bu] Reparse points, if supported by the source filesystem. See \fBREPARSE POINTS AND SYMLINKS\fR for details. .IP \[bu] File and directory creation, access, and modification timestamps. These are stored with Windows' native timestamp resolution of 100 nanoseconds. .IP \[bu] Security descriptors, if supported by the source filesystem and \fB--no-acls\fR is not specified. Note: when not running as an Administrator, security descriptors may be only partially captured (see \fB--strict-acls\fR). .IP \[bu] File attributes, including hidden, sparse, compressed, encrypted, etc. Encrypted files will be stored in encrypted form rather than in plain text. Transparently compressed files will be read as uncompressed and stored subject to the WIM's own compression. There is no special handling for storing sparse files, but they are likely to compress to a small size. .IP \[bu] DOS names (8.3) names of files; however, the failure to read them is not considered an error condition. .IP \[bu] Hard links, if supported by the source filesystem. .IP \[bu] Object IDs, if supported by the source filesystem. .IP \[bu] Extended attributes (EAs), if supported by the source filesystem. .SH REPARSE POINTS AND SYMLINKS A "symbolic link" (or "symlink") is a special file which "points to" some other file or directory. On Windows, a "reparse point" is a generalization of a symlink which allows access to a file or directory to be redirected in a more complex way. Windows uses reparse points to implement symlinks and sometimes uses them for various other features as well. Normally, applications can choose whether they want to "dereference" reparse points and symlinks or not. .PP The default behavior of \fBwimcapture\fR is that reparse points and symlinks are \fInot\fR dereferenced, meaning that the reparse points or symlinks themselves are stored in the archive rather than the files or data they point to. There is a \fB--dereference\fR option, but it is currently only supported by the UNIX version of \fBwimcapture\fR on UNIX filesystems (it's not yet implemented for Windows filesystems). .PP Windows also treats certain types of reparse points specially. For example, Windows applications reading from deduplicated, WIM-backed, or system-compressed files always see the dereferenced data, even if they ask not to. Therefore, \fBwimcapture\fR on Windows will store these files dereferenced, not as reparse points. But \fBwimcapture\fR on UNIX in NTFS-3G mode cannot dereference these files and will store them as reparse points instead. This difference can be significant in certain situations, e.g. when capturing deduplicated files which, to be readable after extraction, require that the chunk store also be present. .SH OPTIONS .TP 6 \fB--boot\fR Mark the new image as the "bootable" image of the WIM. The "bootable" image is the image which the Windows bootloader will use when loading Windows PE from the WIM. .TP \fB--check\fR Include extra integrity information in the resulting WIM. With \fBwimappend\fR, also check the integrity of the WIM before appending to it. Also verify the integrity of any WIMs specified by \fB--update-of\fR and/or \fB--delta-from\fR. .TP \fB--include-integrity\fR Include extra integrity information in the resulting WIM, i.e. like \fB--check\fR but don't do any verification beforehand. .TP \fB--compress\fR=\fITYPE\fR[:\fILEVEL\fR] With \fBwimcapture\fR, use the specified compression format in the new WIM file. \fITYPE\fR may be "none", "XPRESS" (alias: "fast"), "LZX" (alias: "maximum"), or "LZMS" (alias: "recovery"). \fITYPE\fR is matched case-insensitively. The default is "LZX". .IP "" You can optionally also specify an integer compression \fILEVEL\fR. The compression level specifies how hard the compression algorithm for the specified compression \fITYPE\fR will work to compress the data. The values are scaled so that 20 is quick compression, 50 is medium compression, and 100 is high compression. However, you can choose any value and not just these particular values. The default is 50. .IP "" This option only affects the compression type used in non-solid WIM resources. If you are creating a solid WIM (using the \fB--solid\fR option), then you probably want \fB--solid-compress\fR instead. .IP "" Be careful if you choose LZMS compression. It is not compatible with wimlib before v1.6.0, WIMGAPI before Windows 8, DISM before Windows 8.1, and 7-Zip before v15.12. Also note that choosing LZMS compression does not automatically imply solid-mode compression, as it does with DISM. Use \fB--solid\fR if you want to create a solid WIM, or "ESD file". .TP \fB--chunk-size\fR=\fISIZE\fR With \fBwimcapture\fR, use a compression chunk size of \fISIZE\fR bytes. A larger compression chunk size results in a better compression ratio. wimlib supports different chunk sizes depending on the compression type: .RS .IP \[bu] 2 XPRESS: 4K, 8K, 16K, 32K, 64K .IP \[bu] LZX: 32K, 64K, 128K, 256K, 512K, 1M, 2M .IP \[bu] LZMS: 32K, 64K, 128K, 256K, 512K, 1M, 2M, 4M, 8M, 16M, 32M, 64M, 128M, 256M, 512M, 1G .RE .IP "" You can provide the full number (e.g. 32768), or you can use one of the K, M, or G suffixes. KiB, MiB, and GiB are also accepted. .IP "" This option only affects the chunk size used in non-solid WIM resources. If you are creating a solid WIM (using the \fB--solid\fR option), then you probably want \fB--solid-chunk-size\fR instead. .IP "" Use this option with caution if compatibility with Microsoft's WIM software is desired, since their software has limited support for non-default chunk sizes. .TP \fB--solid\fR With \fBwimcapture\fR, create a "solid" WIM file that compresses files together rather than independently. This results in a significantly better compression ratio, but it comes at the cost of slow compression with very high memory usage, reduced compatibility, and slow random access to the resulting WIM file. .IP "" By default this enables solid LZMS compression, thereby creating a file equivalent to one created with DISM's \fB/compress\fR:\fIrecovery\fR option. Such files are also called "ESD files" and were first supported by WIMGAPI in Windows 8, by DISM in Windows 8.1, and by 7-Zip 15.12. .TP \fB--solid-compress\fR=\fITYPE\fR[:\fILEVEL\fR] Like \fB--compress\fR, but set the compression type used in solid resources. The default is LZMS compression. This option only has an effect when \fB--solid\fR is also specified. .TP \fB--solid-chunk-size\fR=\fISIZE\fR Like \fB--chunk-size\fR, but set the chunk size used in solid resources. The default, assuming LZMS compression, is 64MiB (67108864); this requires about 640MiB of memory per thread. This option only has an effect when \fB--solid\fR is also specified. Note: Microsoft's WIM software is not compatible with LZMS chunk sizes larger than 64MiB. .TP \fB--threads\fR=\fINUM_THREADS\fR Number of threads to use for compressing data. Default: autodetect (number of available CPUs). .TP \fB--rebuild\fR With \fBwimappend\fR, rebuild the entire WIM rather than appending the new data to the end of it. Rebuilding the WIM is slower, but will save some space that would otherwise be left as a hole in the WIM. Also see \fBwimoptimize\fR(1). .TP \fB--flags\fR=\fIEDITIONID\fR Specify a string to use in the element of the XML data for the new image. .TP \fB--image-property\fR \fINAME\fR=\fIVALUE\fR Assign an arbitrary property to the new image in the XML document of the WIM. \fIVALUE\fR is the string to set as the property value. \fINAME\fR is the name of the image property, for example "NAME", "DESCRIPTION", or "TOTALBYTES". The name can contain forward slashes to indicate a nested XML element; for example, "WINDOWS/VERSION/BUILD" indicates the BUILD element nested within the VERSION element nested within the WINDOWS element. A bracketed number can be used to indicate one of several identically-named elements; for example, "WINDOWS/LANGUAGES/LANGUAGE[2]" indicates the second "LANGUAGE" element nested within the "WINDOWS/LANGUAGES" element. When adding a list of elements in this way, they must be specified in sequential order. Note that element names are case-sensitive. This option may be specified multiple times. .TP \fB--dereference\fR (UNIX-like systems only) Follow symbolic links and archive the files they point to, rather than archiving the links themselves. .TP \fB--config\fR=\fIFILE\fR Specifies a configuration file (UTF-8 or UTF-16LE encoded; plain ASCII also works) for capturing the new image. The configuration file specifies files that are to be treated specially during the image capture. .IP "" The format of the configuration file is INI-style; that is, it is arranged in bracketed sections. Currently, the following sections are recognized: .RS .IP \[bu] 4 [ExclusionList] --- contains a list of path globs to exclude from capture. If a directory is matched, both the directory and its contents are excluded. .IP \[bu] [ExclusionException] --- contains a list of path globs to include, even when the file or directory also matches a glob in [ExclusionList]. If a directory is matched, then all its contents are included as well. Files or directories \fIwithin\fR a directory excluded by [ExclusionList] may even be included using this, though currently it only works for absolute globs (those that begin with "/" or "\\"); for example, "/dir/file" can be included while "/dir" can be excluded, but including simply "file" won't work in that case. .IP \[bu] [PrepopulateList] --- this does not affect capture, but if the image is applied later with \fB--wimboot\fR, these are globs of files that shall be extracted normally, not as WIMBoot "pointer files". If a directory is matched, all files and subdirectories are also matched recursively. .RE .IP "" Path globs may contain the '*' and '?' meta-characters. Relative globs (e.g. *.mp3) match against a filename in any directory. Absolute globs (e.g. /dir/file), are treated as paths starting at the main directory being captured, or the root of the NTFS volume for NTFS volume capture mode. Do not use drive letters in the paths; they will be ignored. Path separators may be either forwards slashes or backwards slashes. .IP "" Lines beginning with the '#' or ';' characters are treated as comments and ignored. Globs with whitespace in them need not be quoted; however, if they are, both double and single quotes are accepted. .IP "" If this option is not specified the following default configuration file is used: .IP "" .RS .RS .nf [ExclusionList] \\$ntfs.log \\hiberfil.sys \\pagefile.sys \\swapfile.sys \\System Volume Information \\RECYCLER \\$RECYCLE.BIN \\$Recycle.Bin \\Windows\\CSC .RE .RE .fi .IP "" However, special behavior applies if \fB--wimboot\fR is also specified. By default, with \fB--wimboot\fR specified, the file Windows/System32/WimBootCompress.ini in the directory being captured will be used as the configuration file. However, this can be overridden using \fB--config\fR; and this also causes the specified configuration file to be saved in the WIM image as Windows/System32/WimBootCompress.ini, overriding any that may be present on the filesystem. .TP \fB--unix-data\fR (UNIX-like systems only) Store UNIX-specific metadata and special files. This includes: standard UNIX file permissions (owner, group, and mode); device nodes, named pipes, and sockets; and extended attributes (Linux only). This information can later be restored by \fBwimapply\fR with the \fB--unix-data\fR option. .IP UNIX-specific information is ignored by Microsoft's WIM software and by the Windows version of wimlib. .TP \fB--no-acls\fR Do not capture files' security descriptors. .TP \fB--strict-acls\fR Fail immediately if the full security descriptor of any file cannot be read. On Windows, the default behavior without this option is to first try omitting the SACL from the security descriptor, then to try omitting the security descriptor entirely. The purpose of this is to capture as much data as possible without always requiring Administrator privileges. However, if you desire that all security descriptors be captured exactly, you may wish to provide this option, although the Administrator should have permission to read everything anyway. .TP \fB--rpfix\fR, \fB--norpfix\fR Set whether to fix targets of absolute symbolic links (reparse points in Windows terminology) or not. When enabled (\fB--rpfix\fR), absolute symbolic links that point inside the directory tree being captured will be adjusted to be absolute relative to the root of the directory tree being captured. When disabled (\fB--norpfix\fR), absolute symbolic links will be captured exactly as is. .IP "" The default behavior of \fBwimcapture\fR is equivalent to \fB--rpfix\fR. The default behavior of \fBwimappend\fR is equivalent to \fB--rpfix\fR if reparse point fixups have previously been done on \fIWIMFILE\fR, otherwise \fB--norpfix\fR. .IP "" In the case of a multi-source capture, (\fB--source-list\fR specified), passing \fB--norpfix\fR is recommended. Otherwise, reparse point fixups will be disabled on all capture sources destined for non-root locations in the WIM image, while capture sources destined for the WIM root will get the default behavior from the previous paragraph. .TP \fB--source-list\fR \fBwimcapture\fR and \fBwimappend\fR support creating a WIM image from multiple separate files or directories. When \fB--source-list\fR is specified, the \fISOURCE\fR argument specifies the name of a text file, each line of which is either 1 or 2 whitespace separated file paths. The first file path, the source, specifies the path to a file or directory to capture into the WIM image. It may be either absolute or relative to the current working directory. The second file path, if provided, is the target and specifies the path in the WIM image that this file or directory will be saved as. Leading and trailing slashes in the target are ignored, except if it consists entirely of slashes (e.g. "/"), which indicates that the directory is to become the root of the WIM image. If omitted, the target string defaults to the same as the source string. .IP "" An example source list file is as follows: .IP "" .RS .RS .nf # Make the WIM image from the 'winpe' directory winpe / # Send the 'overlay' directory to '/overlay' in the WIM image overlay /overlay # Overlay a separate directory directly on the root of the WIM image. /data/stuff / .RE .RE .fi .IP "" Subdirectories in the WIM are created as needed. Multiple source directories may share the same target, which implies an overlay. In the event that this results a nondirectory file being added to the WIM image multiple times, the last version (as listed in the source list file) overrides any earlier version. .IP "" File paths containing whitespace may be quoted with either single quotes or double quotes. Quotes may not be escaped. .IP "" Lines consisting only of whitespace and lines beginning with '#' preceded by optional whitespace are ignored. .IP "" As a special case, if \fISOURCE\fR is "-", the source list is read from standard input rather than an external file. .IP "" The NTFS volume capture mode on UNIX-like systems cannot be used with \fB--source-list\fR, as only capturing a full NTFS volume is supported. .TP \fB--pipable\fR With \fBwimcapture\fR, create a wimlib-specific "pipable" WIM which can be captured and applied fully sequentially. If \fIWIMFILE\fR is specified as "-", then the pipable WIM is written directly to standard output; otherwise, it is written to disk as usual. The image in the pipable WIM can be later be applied with \fBwimapply\fR, either from disk or from standard input. A typical use of pipable WIMs might involve streaming the WIM image to a remote server when capturing it and/or streaming the WIM image from a remote server when applying it. .IP "" Generally, all the \fBwimlib-imagex\fR commands work on both pipable and non-pipable WIMs. \fBwimoptimize\fR and \fBwimexport\fR may also be used to convert between pipable WIMs and non-pipable WIMs. However, there are a few limitations of pipable WIMs: .RS .IP \[bu] 4 Pipable WIMs are a wimlib extension which are \fInot\fR compatible with Microsoft's WIM software or with other programs such as 7-Zip. .IP \[bu] Using \fBwimappend\fR, multiple images may be added to a pipable WIM. This is supported, though it is less efficient than doing so with non-pipable WIMs because a pipable WIM is fully rebuilt each time it is appended to; and when piping such a WIM to \fBwimapply\fR to extract an image, some unneeded data will be sent over the pipe. .IP \[bu] Although a pipable WIM image may be updated using \fBwimupdate\fR, it requires a full rebuild of the WIM file, making it less efficient than updating a non-pipable WIM. .IP \[bu] Solid pipable WIMs are not yet supported. .RE .TP \fB--not-pipable\fR With \fBwimappend\fR, rebuild the WIM file in the non-pipable (regular) format. This option is only useful if you happen to be adding an image to a pipable WIM (see \fB--pipable\fR) which you want in non-pipable format instead. Note that \fBwimoptimize\fR(1) can also be used to convert between non-pipable and pipable WIMs. .TP \fB--update-of\fR=[\fIWIMFILE\fR:]\fIIMAGE\fR Hint that the image being captured or appended from \fISOURCE\fR is mostly the same as the existing image \fIIMAGE\fR in \fIWIMFILE\fR, but captured at a later point in time, possibly with some modifications in the intervening time. This is designed to be used in incremental backups of the same filesystem or directory tree. \fIIMAGE\fR can be a 1-based index or name of an existing image in \fIWIMFILE\fR. It can also be a negative integer to index backwards into the images (e.g. -1 means the last existing image in \fIWIMFILE\fR). .IP "" When this option is provided, the capture or append of the new image will be optimized by not reading files that, based on metadata such as timestamps, appear not to have been modified since they were archived in the existing \fIIMAGE\fR. Barring manipulation of timestamps, this option only affects performance and does not change the resulting WIM image (but see note below). .IP "" As shown, the full syntax for the argument to this option is to specify the WIM file, a colon, and the image; for example, "--update-of mywim.wim:1". However, the WIM file and colon may be omitted if \fB--delta-from\fR is specified exactly once, in which case the WIM defaults to that specified in \fB--delta-from\fR, or if the operation is \fBwimappend\fR rather \fBwimcapture\fR, in which case the WIM defaults to the one being appended to. .IP "" Note: in the Windows version of wimlib, it has been observed that \fB--update-of\fR mode is not completely reliable at detecting changes in file contents, sometimes causing the old contents of a few files to be archived rather than the current contents. The cause of this problem is that Windows does not immediately update a file's last modification timestamp after every write to that file. Unfortunately, there is no known way for applications like wimlib to automatically work around this bug. Manual workarounds are possible; theoretically, taking any action that causes the problematic files to be closed, such as restarting applications or the computer itself, should cause the files' last modification timestamps to be updated. Also note that wimlib compares file sizes as well as timestamps in determining whether a file has changed, which helps make the problem less likely to occur; and the problem does not occur on other operating systems such as Linux which maintain files' last modification timestamps correctly. .TP \fB--delta-from\fR=\fIWIMFILE\fR Capture or append the new image as a "delta" from \fIWIMFILE\fR. Any file data that would ordinarily need to be archived in the new or updated WIM is omitted if it is already present in the \fIWIMFILE\fR on which the delta is being based. The resulting WIM will still contain a full copy of the image metadata, but this is typically only a small fraction of a WIM's total size. .IP "" This option can be specified multiple times, in which case the resulting delta WIM will only contain file data not present in any of the specified base WIMs. .IP "" To operate on the resulting delta WIM using other commands such as \fBwimapply\fR, you must specify the delta WIM as the WIM file to operate on, but also reference the base WIM(s) using the \fB--ref\fR option. Beware: to retain the proper functioning of the delta WIM, you can only add, not delete, files and images to the base WIM(s) following the capture of a delta from it. .IP "" \fB--delta-from\fR may be combined with \fB--update-of\fR to increase the speed of capturing a delta WIM. .IP "" As an example, consider the following backup and restore sequence: .IP "" .RS .nf (initial backup) $ wimcapture /some/directory bkup-base.wim (some days later, create second backup as delta from first) $ wimcapture /some/directory bkup-2013-08-20.dwm \\ --update-of bkup-base.wim:-1 --delta-from bkup-base.wim (restoring the second backup) $ wimapply bkup-2013-08-20.dwm --ref=bkup-base.wim 1 \\ /some/directory .RE .fi .IP "" However, note that as an alternative to the above sequence that used a delta WIM, the second backup could have simply been appended to the WIM as new image using \fBwimappend\fR. Delta WIMs should be used only if it's desired to base the backups or images on a separate, large file that is rarely modified. .IP "" \fB--delta-from\fR is supported by both \fBwimcapture\fR and \fBwimappend\fR. .IP "" Delta WIMs are compatible with Microsoft's WIM software. For example, you can use the /ref option of ImageX to reference the base WIM(s), similar to above. .IP "" Additional note: wimlib is generalized enough that you can in fact combine \fB--pipable\fR and \fB--delta-from\fR to create pipable delta WIMs. In such cases, the base WIM(s) must be captured as pipable as well as the delta WIM, and when applying an image, the base WIM(s) must be sent over the pipe after the delta WIM. .TP \fB--wimboot\fR Mark the image as WIMBoot-compatible. See Microsoft's documentation for more information about WIMBoot. With \fBwimcapture\fR this option will set the compression type to XPRESS and the chunk size to 4096 bytes; these can, however, still be overridden through the \fB--compress\fR and \fB--chunk-size\fR parameters, respectively. In addition, this option will set the configuration file to \fISOURCE\fR\\Windows\\System32\\WimBootCompress.ini if present and accessible; however, this may still be overridden through the \fB--config\fR parameter. .TP \fB--unsafe-compact\fR With \fBwimappend\fR, compact the WIM archive in-place and append any new data, eliminating "holes". This is efficient, but in general this option should \fInot\fR be used because a failed or interrupted compaction will corrupt the WIM archive. For more information, see the documentation for this option to \fBwimoptimize\fR(1). .TP \fB--snapshot\fR Create a temporary filesystem snapshot of the source directory and capture the files from it. Currently, this option is only supported on Windows, where it uses the Volume Shadow Copy Service (VSS). Using this option, you can create a consistent backup of the system volume of a running Windows system without running into problems with locked files. For the VSS snapshot to be successfully created, \fBwimlib-imagex\fR must be run as an Administrator, and it cannot be run in WoW64 mode (i.e. if Windows is 64-bit, then \fBwimlib-imagex\fR must be 64-bit as well). .TP \fB--create\fR With \fBwimappend\fR, if the WIM file doesn't exist yet, then create it (like \fBwimcapture\fR). .SH NOTES \fBwimappend\fR does not support appending an image to a split WIM. .PP Except when using \fB--unsafe-compact\fR, it is safe to abort a \fBwimappend\fR command partway through; however, after doing this, it is recommended to run \fBwimoptimize\fR to remove any data that was appended to the physical WIM file but not yet incorporated into the structure of the WIM, unless the WIM was being fully rebuilt (e.g. with \fB--rebuild\fR), in which case you should delete the temporary file left over. .PP \fBwimlib-imagex\fR creates WIMs compatible with Microsoft's software (WIMGAPI, ImageX, DISM), with some caveats: .IP \[bu] 4 With \fBwimlib-imagex\fR on UNIX-like systems, it is possible to create a WIM image containing files with names differing only in case, or files with names containing the characters ':', '*', '?', '"', '<', '>', '|', or '\\', which are valid on POSIX-compliant filesystems but not Windows. Be warned that such files will not be extracted by default by the Windows version of \fBwimlib-imagex\fR, and (even worse) Microsoft's ImageX can be confused by such names and quit extracting the image partway through. .IP \[bu] Pipable WIMs are incompatible with Microsoft's software. Pipable WIMs are created only if \fIWIMFILE\fR was specified as "-" (standard output) or if the \fB--pipable\fR flag was specified. .IP \[bu] WIMs captured with a non-default chunk size (with the \fB--chunk-size\fR option) or as solid archives (with the \fB--solid\fR option) or with LZMS compression (with \fB--compress\fR=LZMS or \fB--compress\fR=recovery) have varying levels of compatibility with Microsoft's software. Generally, more recent versions of Microsoft's software are more compatible. .SH EXAMPLES First example: Create a new WIM 'mywim.wim' with LZX ("maximum") compression that will contain a captured image of the directory tree 'somedir'. Note that the image name need not be specified and will default to 'somedir': .RS .PP wimcapture somedir mywim.wim .RE .PP Next, append the image of a different directory tree to the WIM created above: .RS .PP wimappend anotherdir mywim.wim .RE .PP Easy enough, and the above examples of imaging directory trees work on both UNIX-like systems and Windows. Next, capture a WIM with several non-default options, including XPRESS ("fast") compression, extra integrity information, no messing with absolute symbolic links, and an image name and description: .RS .PP wimcapture somedir mywim.wim --compress=fast \\ .RS --check --norpfix "Some Name" "Some Description" .RE .RE .PP On a UNIX-like system, capture a full NTFS volume into a new WIM using the \fBNTFS VOLUME CAPTURE (UNIX)\fR mode, and name the image "Windows 7": .RS .PP wimcapture /dev/sda2 windows7.wim "Windows 7" .RE .PP or, on Windows, to capture a full NTFS volume you instead need to specify the root directory of the mounted volume, for example: .RS .PP wimcapture E:\\ windows7.wim "Windows 7" .RE .PP Same as UNIX example above, but capture the WIM in the wimlib-specific "pipable" format that can be piped to \fBwimapply\fR: .RS .PP wimcapture /dev/sda2 windows7.wim "Windows 7" --pipable .RE .PP Same as above, but instead of writing the pipable WIM to the file "windows7.wim", write it directly to standard output through a pipe into some other program "someprog", which could, for example, be a program or script that streams the data to a server: .RS .PP wimcapture /dev/sda2 - "Windows 7" | someprog .RE .SH SEE ALSO .BR wimlib-imagex (1), .BR wimapply (1) .BR wimoptimize (1) wimlib-1.13.1/doc/man1/wimlib-imagex.10000644000175000017500000002667213464166215014244 00000000000000.TH WIMLIB-IMAGEX 1 "May 2019" "wimlib 1.13.1" "User Commands" .SH NAME wimlib-imagex \- Extract, create, modify, or mount a WIM archive .SH SYNOPSIS \fBwimlib-imagex append\fR \fIarguments...\fR (or \fBwimappend\fR \fIarguments...\fR) .br \fBwimlib-imagex apply\fR \fIarguments...\fR (or \fBwimapply\fR \fIarguments...\fR) .br \fBwimlib-imagex capture\fR \fIarguments...\fR (or \fBwimcapture\fR \fIarguments...\fR) .br \fBwimlib-imagex delete\fR \fIarguments...\fR (or \fBwimdelete\fR \fIarguments...\fR) .br \fBwimlib-imagex dir\fR \fIarguments...\fR (or \fBwimdir\fR \fIarguments...\fR) .br \fBwimlib-imagex export\fR \fIarguments...\fR (or \fBwimexport\fR \fIarguments...\fR) .br \fBwimlib-imagex extract\fR \fIarguments...\fR (or \fBwimextract\fR \fIarguments...\fR) .br \fBwimlib-imagex info\fR \fIarguments...\fR (or \fBwiminfo\fR \fIarguments...\fR) .br \fBwimlib-imagex join\fR \fIarguments...\fR (or \fBwimjoin\fR \fIarguments...\fR) .br \fBwimlib-imagex mount\fR \fIarguments...\fR (or \fBwimmount\fR \fIarguments...\fR) .br \fBwimlib-imagex mountrw\fR \fIarguments...\fR (or \fBwimmountrw\fR \fIarguments...\fR) .br \fBwimlib-imagex optimize\fR \fIarguments...\fR (or \fBwimoptimize\fR \fIarguments...\fR) .br \fBwimlib-imagex split\fR \fIarguments...\fR (or \fBwimsplit\fR \fIarguments...\fR) .br \fBwimlib-imagex unmount\fR \fIarguments...\fR (or \fBwimunmount\fR \fIarguments...\fR) .br \fBwimlib-imagex update\fR \fIarguments...\fR (or \fBwimupdate\fR \fIarguments...\fR) .br \fBwimlib-imagex verify\fR \fIarguments...\fR (or \fBwimverify\fR \fIarguments...\fR) .SH DESCRIPTION \fBwimlib-imagex\fR deals with archive files in the Windows Imaging (WIM) format. Its interface is similar to Microsoft's ImageX, but \fBwimlib-imagex\fR is cross-platform and has useful improvements and extensions. .PP To do its work, \fBwimlib-imagex\fR uses \fBwimlib\fR, an open source C library that provides interfaces for manipulating WIM archives. wimlib is completely independent from the equivalent Microsoft implementation (WIMGAPI, or wimgapi.dll). You can use wimlib in your own programs, although for command-line use \fBwimlib-imagex\fR already provides access to most of wimlib's functionality. .SH BACKGROUND INFORMATION The Windows Imaging (WIM) format was designed by Microsoft primarily for archiving Windows filesystems, such as NTFS. However, it can be used on other platforms as well, with some limitations. A WIM archive contains one or more images, each of which is a logically independent directory tree. Images are indexed starting from 1, and each may also have a name. File data is stored as content-addressable "blobs" that are deduplicated across the entire archive. Data may be compressed using one of several compression algorithms. .PP An update of the WIM format which Microsoft released with Windows 8 uses solid-mode LZMS compression to achieve a better compression ratio. Such files are also called "ESD files" and may have the \.esd extension instead of .wim. wimlib fully supports these files except when they are encrypted. .SH COMMANDS \fBwimlib-imagex\fR accepts one of a number of commands (listed above in \fBSYNOPSYS\fR), and additional arguments depending on the specific command. Although \fBwimlib-imagex\fR will print usage information with \fB--help\fR or if you invoke it incorrectly, the full documentation for each \fBwimlib-imagex\fR command can be found in the appropriate manual page. .PP Note: if appropriate hard links or batch files have been installed, a command \fBwimlib-imagex \fICOMMAND\fR can also be accessed as simply \fBwim\fICOMMAND\fR; for example, \fBwimapply\fR for \fBwimlib-imagex apply\fR. For brevity the documentation uses the shorter names. .SH GENERAL FEATURES The following are some of the general features, or use cases, currently supported by \fBwimlib-imagex\fR, and pointers to the relevant commands: .IP \[bu] 4 Display information about a WIM file (\fBwiminfo\fR) .IP \[bu] List the files in a WIM image (\fBwimdir\fR) .IP \[bu] Extract, or "apply", a full WIM image (\fBwimapply\fR) .IP \[bu] Extract files or directories from a WIM image (\fBwimextract\fR) .IP \[bu] 4 Capture a WIM image and save it to a new WIM file (\fBwimcapture\fR) .IP \[bu] Capture a WIM image and append it to an existing WIM file (\fBwimappend\fR) .IP \[bu] Modify a WIM image by adding, deleting, or renaming files (\fBwimupdate\fR) .IP \[bu] (Linux only) Mount a WIM image read-only (\fBwimmount\fR) .IP \[bu] (Linux only) Mount a WIM image read-write (\fBwimmountrw\fR) .IP \[bu] Delete an image from a WIM file (\fBwimdelete\fR) .IP \[bu] Export image(s) from a WIM file (\fBwimexport\fR) .IP \[bu] Change the name or description of a WIM image (\fBwiminfo\fR) .IP \[bu] Change the bootable image index of a WIM file (\fBwiminfo\fR) .IP \[bu] Rebuild, and optionally recompress, a WIM file (\fBwimoptimize\fR) .IP \[bu] Split a WIM file into multiple parts (\fBwimsplit\fR) .IP \[bu] Join a split WIM (\fBwimjoin\fR) .IP \[bu] Verify the validity and integrity of a WIM file (\fBwimverify\fR) .SH DETAILED FEATURES This section presents some of the interesting features of \fBwimlib-imagex\fR in more detail. .IP \[bu] 4 Multi-platform support. \fBwimlib-imagex\fR is supported on both UNIX-like systems (mainly Linux, but also FreeBSD, Mac OS X, etc.) and Windows. Most code is shared among all platforms, but platform-specific features are still supported when possible. .IP \[bu] XPRESS, LZX, and LZMS compression and decompression. wimlib contains advanced implementations of all these compression algorithms. These have been improved over time and now usually outperform and outcompress their Microsoft equivalents, while remaining fully compatible. .IP \[bu] Solid-mode compression, or "ESD file", support. "ESD files" are an updated WIM format that uses solid LZMS compression to achieve a better compression ratio. .IP \[bu] Multithreaded compression. By default, wimlib's data compression is multithreaded and will use all available processors. .IP \[bu] On UNIX-like systems, integration with libntfs-3g allows capturing a WIM image directly from an NTFS volume, or applying a WIM image directly to an NTFS volume. This allows saving and restoring NTFS-specific data and metadata, such as security descriptors and named data streams, which would otherwise only be supported on Windows. .IP \[bu] On UNIX-like systems, optional support for saving and restoring standard UNIX file permissions (owner/group/mode), UNIX special files, and extended attributes. (This is a wimlib extension; Microsoft's WIM software ignores this extra information.) .IP \[bu] On Linux, support for mounting WIM images with FUSE (Filesystem in UserSpacE), both readonly and read-write. .IP \[bu] Split WIMs. A split WIM is a WIM archive split into multiple parts. \fBwimsplit\fR can create a split WIM from a standalone WIM, and \fBwimjoin\fR can create a standalone WIM from a split WIM. .IP \[bu] Delta WIMs. A delta WIM contains image metadata but excludes file data already present in another WIM file. A delta WIM can be created using \fBwimcapture\fR with the \fB--delta-from\fR option. .IP \[bu] "Pipable" WIMs. As a wimlib extension (not compatible with the Microsoft implementation), \fBwimcapture\fR supports capturing a WIM file to standard output in a special "pipable" format which can later be applied by sending it to \fBwimapply\fR on standard input. Among other things, this can be used to pipe images to or from a server over the network to implement fast filesystem imaging and restore. .IP \[bu] Support for WIM integrity tables. Although file data in WIM archives is always checksummed, there can also be an extra set of checksums (an "integrity table") associated with the WIM file itself to provide extra integrity assurance. The \fB--check\fR option to several \fBwimlib-imagex\fR commands can be used to verify or add these extra checksums. .IP \[bu] Fast incremental backups. Because WIM archives use content-addressible file data, the contents of files are automatically deduplicated. In addition, using the \fB--update-of\fR option of \fBwimcapture\fR or \fBwimappend\fR, you can optimize an image capture so that files that are unmodified based on timestamps are not even read from disk. .IP \[bu] Windows-specific image metadata support. When capturing an image of a Windows operating system, wimlib will automatically populate XML metadata fields such as the Windows OS version details by scanning well-known system files. .IP \[bu] WIMBoot support. On Windows 8.1 and later, files can be "externally backed" by a WIM archive with the help of Microsoft's Windows Overlay Filesystem (WOF) filter driver. With the \fB--wimboot\fR option, \fBwimapply\fR will extract "pointer files" to the WIM archive rather than the files themselves. .IP \[bu] VSS snapshot support. On Windows, \fBwimcapture\fR or \fBwimappend\fR with the \fB--snapshot\fR option will automatically create a temporary VSS snapshot and capture the image from it. This can be used to image a "live" Windows system. .IP \[bu] Long path support on Windows. \fBwimlib-imagex\fR can capture and apply files with paths exceeding the MAX_PATH (260 character) limitation of the Win32 subsystem. .IP \[bu] Non-Administrator support on Windows. You can run \fBwimlib-imagex\fR without Administrator rights, subject to some limitations. .SH COMMON OPTIONS The following options work for all \fBwimlib-imagex\fR commands: .TP 6 \fB--help\fR Display the help, then exit. .TP \fB--version\fR Display the version and legal information, then exit. .TP \fB--quiet\fR Suppress informational and progress messages. .SH CASE SENSITIVITY By default, the case sensitivity of \fBwimlib-imagex\fR differs somewhat between UNIX-like systems and Windows. WIM images may (but usually do not) have multiple files with the same case-insensitive name. Internally, wimlib stores filenames as case-sensitive, but on Windows paths actually provided by the user for use in a WIM image (e.g. for extracting, adding, renaming, or deleting files) will by default be treated as case-insensitive in order to get the "expected" behavior. This differs from the default behavior on UNIX-like systems, where such paths will be treated as case-sensitive. .PP Note that with case insensitivity, a path component may in general be ambiguous due to multiple files or directories having the same case-insensitive name. In such cases, if there is a file or directory with an exactly matching name, it is chosen; otherwise, one of the case-insensitively matching file or directories is chosen arbitrarily. .PP The default case sensitivity of \fBwimlib-imagex\fR can be overridden by explicitly setting the environmental variable \fBWIMLIB_IMAGEX_IGNORE_CASE\fR to 1, in which case such paths will be treated case insensitively, or 0, in which such paths will be treated case sensitively. .PP Regardless of these settings, options and non-path arguments must be specified in lower case. .SH LICENSE wimlib-imagex may be redistributed and/or modified under the terms of the GNU General Public License; either version 3 of the License, or (at your option) any later version. There is NO WARRANTY, to the extent permitted by law. .SH REPORTING BUGS Report bugs to ebiggers3@gmail.com or to \fIhttps://wimlib.net/forums/\fR. Feedback and suggestions are also welcome. .SH SEE ALSO .BR wimappend (1), .BR wimapply (1), .BR wimcapture (1), .BR wimdelete (1), .BR wimdir (1), .BR wimexport (1), .BR wimextract (1), .BR wiminfo (1), .BR wimjoin (1), .BR wimmount (1), .BR wimmountrw (1), .BR wimoptimize (1), .BR wimsplit (1), .BR wimunmount (1), .BR wimupdate (1), .BR wimverify (1), wimlib-1.13.1/doc/man1/wimappend.10000644000175000017500000000002613464166054013457 00000000000000.so man1/wimcapture.1 wimlib-1.13.1/doc/man1/wimmount.10000644000175000017500000002332013464166215013353 00000000000000.TH WIMMOUNT "1" "May 2019" "wimlib 1.13.1" "User Commands" .SH NAME wimmount, wimmountrw, wimunmount \- Mount or unmount a WIM image .SH SYNOPSIS \fBwimmount\fR \fIWIMFILE\fR [\fIIMAGE\fR] \fIDIRECTORY\fR [\fIOPTION\fR...] .br \fBwimmountrw\fR \fIWIMFILE\fR [\fIIMAGE\fR] \fIDIRECTORY\fR [\fIOPTION\fR...] .br \fBwimunmount\fR \fIDIRECTORY\fR [\fIOPTION\fR...] .SH DESCRIPTION On Linux, the \fBwimmount\fR (equivalently: \fBwimlib-imagex mount\fR) and \fBwimmountrw\fR (equivalently: \fBwimlib-imagex mountrw\fR) commands mount the specified \fIIMAGE\fR in the Windows Imaging (WIM) archive \fIWIMFILE\fR on the directory \fIDIRECTORY\fR using FUSE (Filesystem in Userspace). \fBwimmount\fR will mount the image read-only, while \fBwimmountrw\fR will mount the image read-write. .PP \fIIMAGE\fR specifies the image in \fIWIMFILE\fR to mount. It may be the 1-based index of an image or the name of an image. It may be omitted if \fIWIMFILE\fR contains only one image. You can use \fBwiminfo\fR(1) to list the images contained in \fIWIMFILE\fR. .PP The WIM image can be unmounted using \fBwimunmount\fR (equivalently: \fBwimlib-imagex unmount\fR). Changes made to an image mounted read-write will be discarded unless the \fB--commit\fR flag is provided to \fBwimunmount\fR. .SH DATA AND METADATA SUPPORT WIM images can contain a variety of types of files and file metadata, some of which is Windows-specific. Currently, the mount feature can translate some, but not all, Windows concepts to Linux equivalents. Briefly, the following features are \fIsupported\fR (read/write): .IP \[bu] 4 Hard links .IP \[bu] Symbolic links. Native Windows symbolic links and junctions in a mounted WIM image will automatically be translated into UNIX symbolic links, potentially with their targets fixed to be valid given the actual mountpoint directory. UNIX symbolic links created in a read-write mounted WIM image will automatically be translated into native Windows symbolic links. .IP \[bu] Named data streams (mostly). See the \fB--streams-interface\fR option. .PP In addition, standard UNIX file permissions (owner, group, and mode) and special files are supported if the \fB--unix-data\fR option is used. .PP However, the following features are \fIunsupported\fR and not exposed in mounted images: .IP \[bu] 4 Windows security descriptors. New files are not given security descriptors. .IP \[bu] DOS names (8.3 names) (short names). New files are not given DOS names. .IP \[bu] Windows file attributes. New files are assigned default attributes based on the UNIX file mode bits. .IP \[bu] Object IDs. New files are not given object IDs. .IP \[bu] EFS-encrypted files. The files themselves will be visible in mounted WIM images but their data will not be available. .IP \[bu] Extended attributes. Although wimlib supports WIM images containing extended attributes, these are not yet exposed in mounted WIM images. (This may be implemented in the future, though it would conflict with the use of extended attributes to expose Windows concepts like named data streams.) .SH SPLIT WIMS You may use \fBwimmount\fR to mount an image from a split WIM read-only. However, you may not mount an image from a split WIM read-write. .PP The \fIWIMFILE\fR argument must specify the first part of the split WIM, while the additional parts of the split WIM must be specified in one or more \fB--ref\fR="\fIGLOB\fR" options. Since globbing is built into the \fB--ref\fR option, typically only one \fB--ref\fR option is necessary. For example, the names for the split WIM parts usually go something like: .PP .RS .nf mywim.swm mywim2.swm mywim3.swm mywim4.swm mywim5.swm .RE .PP To mount the first image of this split WIM to the directory "dir", run: .PP .RS wimmount mywim.swm 1 dir --ref="mywim*.swm" .RE .PP .SH NOTES \fIAvailability\fR: Mounting WIM images is only supported on Linux-based systems. These commands will not work on other platforms. Furthermore, the library cannot have been configured \fB--without-fuse\fR. .PP \fIMultiple mounts\fR: You are free to mount many WIM images at the same time, provided that there are not two images mounted read-write from the same file at the same time. .PP \fIAppends vs. rebuilds\fR: By default, changes to a read-write WIM are made in-place by appending to the WIM. This is nice for big WIM files, since the entire file doesn't have to be rebuilt to make a small change. But, if you are making many changes to a read-write mounted WIM, especially deleting large files, it is suggested to provide the \fB--rebuild\fR option to \fBwimunmount\fR to force the WIM to be rebuilt, or else run \fBwimoptimize\fR afterwards. .PP \fIESD files (solid WIMs)\fR: You can mount version 3584 WIMs, which usually contain LZMS-compressed solid resources and may carry the \fI.esd\fR file extension rather than \fI.wim\fR. However, such files are not designed for random access, so reading data from them when mounted may be very slow. In addition, \fI.esd\fR files downloaded directly by the Windows 8 web downloader have encrypted segments, and wimlib cannot mount such files until they are first decrypted. .SH MOUNT OPTIONS .TP 6 \fB--check\fR Before mounting the WIM image, verify the integrity of the WIM if it contains extra integrity information. .TP \fB--streams-interface\fR=\fIINTERFACE\fR This option is inspired by the \fBntfs-3g\fR(8) filesystem driver. It controls how named data streams (also called "alternate data streams") in WIM files are made available. .IP "" If "none", it will be impossible to read or write the named data streams. .IP "" If "xattr" (default), named data streams will be accessible through extended file attributes, unless this support was disabled when compiling wimlib. The named data streams may be accessed through extended attributes named "user.*", where the * is the name of the named data stream. See \fBsetfattr\fR(1) and \fBgetfattr\fR(1). Note that this is not an ideal interface, since named data streams may be larger than the maximum allowed extended attribute size. .IP "" If "windows", the named data streams will be accessible by specifying the filename, then a colon, then the name of the named data stream; for example, "myfile:mystream". .TP \fB--debug\fR Turn on debugging information printed by the FUSE library, and do not fork into the background. .TP \fB--ref\fR="\fIGLOB\fR" File glob of additional WIMs or split WIM parts to reference resources from. See \fBSPLIT_WIMS\fR. This option can be specified multiple times. Note: \fIGLOB\fR is listed in quotes because it is interpreted by \fBwimlib-imagex\fR and may need to be quoted to protect against shell expansion. .TP \fB--staging-dir\fR=\fIDIR\fR Store temporary staging files in a subdirectory of the directory \fIDIR\fR. Only valid for \fBwimmountrw\fR. .TP \fB--unix-data\fR Honor UNIX-specific metadata that was captured by \fBwimcapture\fR with the \fB--unix-data option\fR. By default, \fBwimmount\fR (and \fBwimmountrw\fR) will ignore both Windows-style security descriptors and UNIX-specific metadata. In this default mode, all files will simply be owned by the user running \fBwimmount\fR and will have mode 0777. (They will still not be accessible to other users unless you also specify \fB--allow-other\fR.) If you instead provide the \fB--unix-data\fR option, these default permissions will be overridden on a per-file basis with the UNIX-specific metadata from the WIM image when available, and in the case of \fBwimmountrw\fR it will also be possible to change the UNIX permissions on files in the mounted image using the standard UNIX tools and functions, and (if appropriately privileged) create UNIX special files such as device nodes. .TP \fB--allow-other\fR Pass the \fBallow_other\fR option to the FUSE mount. See \fBmount.fuse\fR (8). Note: to do this as a non-root user, \fBuser_allow_other\fR needs to be specified in /etc/fuse.conf. .SH UNMOUNT OPTIONS .TP \fB--commit\fR Update the WIM file with the changes that have been made. Has no effect if the mount is read-only. .TP \fB--force\fR In combination with \fB--commit\fR, force the WIM image to be committed even if there are open file descriptors to the WIM image. Any such file descriptors will be immediately closed, and the WIM image will be committed and unmounted. .TP \fB--check\fR If committing changes to the WIM, include extra integrity information, even if it was not present before. .TP \fB--rebuild\fR Rebuild the entire WIM rather than appending any new data to the end of it. Rebuilding the WIM is slower, but will save a little bit of space that would otherwise be left as a hole in the WIM. Even more space will be saved if the read-write mount resulted in streams being deleted from the WIM. Also see .TP \fB--new-image\fR In combination with \fB--commit\fR for a read-write mounted image, causes the modified image to be committed as a new, unnamed image appended to the WIM archive. The original image will be unmodified. .SH IMPLEMENTATION DETAILS Since a WIM is an archive and not a filesystem per se, \fBwimmountrw\fR creates a temporary staging directory to contain files that are created or modified. This directory is located in the same directory as \fIWIMFILE\fR by default, but the location can be set using the \fB--staging-dir\fR option. When the filesystem is unmounted with \fB--commit\fR, the WIM is modified in-place (or rebuilt completely with \fB--rebuild\fR), merging in the staging files as needed. Then, the temporary staging directory is deleted. .PP \fBwimunmount\fR runs in a separate process from the process that previously ran \fBwimmount\fR. When unmounting a read-write mounted WIM image with \fB--commit\fR, these two processes communicate using a POSIX message queue so that the unmount process can track the progress of the mount process. See \fIsrc/mount_image.c\fR in the source code for details. .SH SEE ALSO .BR wimlib-imagex (1) wimlib-1.13.1/doc/man1/wimoptimize.10000644000175000017500000001225313464166215014054 00000000000000.TH WIMOPTIMIZE "1" "May 2019" "wimlib 1.13.1" "User Commands" .SH NAME wimoptimize \- Optimize a WIM archive .SH SYNOPSIS \fBwimoptimize\fR \fIWIMFILE\fR [\fIOPTION\fR...] .SH DESCRIPTION \fBwimoptimize\fR, or equivalently \fBwimlib-imagex optimize\fR, rebuilds the standalone WIM archive \fIWIMFILE\fR. The new WIM is written to a temporary file, and it is renamed to the original file when it's ready. This will remove any holes that have been left in the WIM as a result of appending or deleting files or images, so the new WIM may be smaller than the old WIM. .PP By default, \fBwimoptimize\fR will reuse (not recompress) compressed data and will not change the solid or pipable status of the WIM. However, it can also perform recompression and/or convert between solid, non-solid, pipable, and non-pipable WIMs; see the options and examples below. .SH OPTIONS .TP 6 \fB--check\fR Before optimizing the WIM, verify its integrity if it contains extra integrity information. Also include extra integrity information in the optimized WIM, even if it was not present before. .TP \fB--nocheck\fR Do not include extra integrity information in the optimized WIM, even if it was present before. .TP \fB--include-integrity\fR Include extra integrity information in the optimized WIM, i.e. like \fB--check\fR but don't also verify the WIM beforehand. .TP \fB--recompress\fR Recompress all data in the WIM while optimizing it. This will significantly increase the time needed to optimize the WIM, but it may result in a better compression ratio if wimlib can do a better job than the program that created the WIM --- which is likely the case if the WIM was Microsoft-created, as wimlib's compressors are slightly stronger. .TP \fB--compress\fR=\fITYPE\fR[:\fILEVEL\fR] Recompress the WIM using the specified compression type, and optionally the specified compression level for that compression type. This implies \fB--recompress\fR. See the documentation for this option to \fBwimcapture\fR(1) for more details. .TP \fB--chunk-size\fR=\fISIZE\fR Set the WIM compression chunk size to \fISIZE\fR. See the documentation for this option to \fBwimcapture\fR(1) for more details. .TP \fB--solid\fR Create a "solid" archive that compresses multiple files together. This usually results in a significantly better compression ratio but has disadvantages such as reduced compatibility. See the documentation for this option to \fBwimcapture\fR(1) for more details. .TP \fB--solid-compress\fR=\fITYPE\fR[:\fILEVEL\fR] Like \fB--compress\fR, but set the compression type used in solid resources. See the documentation for this option to \fBwimcapture\fR(1) for more details. .TP \fB--solid-chunk-size\fR=\fISIZE\fR Like \fB--chunk-size\fR, but set the chunk size used in solid resources. See the documentation for this option to \fBwimcapture\fR(1) for more details. .TP \fB--threads\fR=\fINUM_THREADS\fR Number of threads to use for compressing data. Default: autodetect (number of processors). .TP \fB--pipable\fR Rebuild the WIM so that it can be applied fully sequentially, including from a pipe. See \fBwimcapture\fR(1) for more details about creating pipable WIMs. By default, when neither \fB--pipable\fR or \fB--not-pipable\fR is specified, the optimized WIM will be pipable if and only if it was pipable before. .TP \fB--not-pipable\fR Rebuild the WIM in the non-pipable format. .TP \fB--unsafe-compact\fR Compact the WIM in-place, without using a temporary file. Existing resources are shifted down to fill holes and new resources are appended as needed. The WIM is truncated to its final size, which may shrink the on-disk file. This is more efficient than a full rebuild, but it is only supported when no recompression is being done. More importantly, AN UNSAFE COMPACTION OPERATION CANNOT BE SAFELY INTERRUPTED! If the operation is interrupted, then the WIM will be corrupted, and it may be impossible (or at least very difficult) to recover any data from it. Users of this option are expected to know what they are doing and assume responsibility for any data corruption that may result. .SH NOTES \fBwimoptimize\fR does not support split WIMs or delta WIMs. For such files, consider using \fBwimexport\fR(1) instead. Note that \fBwimoptimize\fR is roughly equivalent to: .RS .PP \fBwimexport\fR \fIWIMFILE\fR all tmp.wim && mv tmp.wim \fIWIMFILE\fR .RE .PP .SH EXAMPLES Rebuild 'install.wim': .RS .PP wimoptimize install.wim .RE .PP Rebuild and recompress 'install.wim': .RS .PP wimoptimize install.wim --recompress .RE .PP Rebuild and recompress 'install.wim' using LZX ("maximum") compression at a higher-than-default compression level. The compression chunk size remains unchanged. This command will be slow, but it might be useful for optimizing files for distribution. See \fIhttps://wimlib.net/compression.html\fR for some benchmark results. .RS .PP wimoptimize install.wim --compress=LZX:100 .RE .PP Recompress 'install.wim' using solid-mode compression, then rename it to \'install.esd\'. This will decrease the archive size significantly. (Also consider using 'wimexport install.wim all install.esd --solid'.): .RS .PP wimoptimize install.wim --solid .br mv install.wim install.esd .RE .SH SEE ALSO .BR wimlib-imagex (1) .BR wimexport (1) .BR wimverify (1) wimlib-1.13.1/doc/man1/wimjoin.10000644000175000017500000000263713464166215013160 00000000000000.TH WIMJOIN "1" "May 2019" "wimlib 1.13.1" "User Commands" .SH NAME wimjoin\- Join a split WIM into a standalone WIM .SH SYNOPSIS \fBwimjoin\fR [\fIOPTION\fR...] \fIOUT_WIMFILE\fR \fISPLIT_WIM_PART\fR... .SH DESCRIPTION \fBwimjoin\fR, or equivalently \fBwimlib-imagex join\fR, joins the \fISPLIT_WIM_PARTs\fR into a standalone (one-part) WIM \fIOUT_WIMFILE\fR. All parts of the split WIM must be specified; you probably want to do so using a shell wildcard. .SH OPTIONS .TP 6 \fB--check\fR When reading each \fISPLIT_WIM_PART\fR, verify its integrity if it contains extra integrity information. In addition, include extra integrity information in \fIOUT_WIMFILE\fR, even if the split WIM parts did not contain this information. .TP \fB--include-integrity\fR Include extra integrity information in \fIOUT_WIMFILE\fR, i.e. like \fB--check\fR but don't also verify the split WIM parts beforehand. .SH EXAMPLES Join a split WIM, with the parts named `windows*.swm' where the * is anything (usually the number of the part, except for the first part which may have no number), and write the joined WIM to the file `windows.wim'. .RS .PP wimjoin windows.wim windows*.swm .RE .SH NOTES Both non-pipable and pipable split WIMs may be joined. .PP \fBwimjoin\fR is roughly equivalent to: .RS .PP \fBwimexport\fR \fISWM_PART_1\fR --ref="\fISWM_GLOB\fR" all \fIOUT_WIMFILE\fR .RE .SH SEE ALSO .BR wimlib-imagex (1) .BR wimexport (1) .BR wimsplit (1) wimlib-1.13.1/doc/man1/wimunmount.10000644000175000017500000000002413464166054013713 00000000000000.so man1/wimmount.1 wimlib-1.13.1/doc/man1/wimexport.10000644000175000017500000002056313464166215013540 00000000000000.TH WIMEXPORT "1" "May 2019" "wimlib 1.13.1" "User Commands" .SH NAME wimexport \- Export image(s) from a WIM archive .SH SYNOPSIS \fBwimexport\fR \fISRC_WIMFILE\fR \fISRC_IMAGE\fR \fIDEST_WIMFILE\fR [\fIDEST_IMAGE_NAME\fR [\fIDEST_IMAGE_DESC\fR]] [\fIOPTION\fR...] .SH DESCRIPTION \fBwimexport\fR, or equivalently \fBwimlib-imagex export\fR, exports the specified image from \fISRC_WIMFILE\fR into \fIDEST_WIMFILE\fR, optionally changing the image's name and/or description. If \fIDEST_WIMFILE\fR already exists, the image will be appended to it; otherwise, a new WIM archive will be created to contain the exported image. .PP \fISRC_IMAGE\fR specifies the image in \fISRC_WIMFILE\fR to export. It may be the 1-based index of an image, the name of an image, or the keyword "all" to specify all images. You can use \fBwiminfo\fR(1) to list the images contained in \fISRC_WIMFILE\fR. .PP If specified, \fIDEST_IMAGE_NAME\fR is the name to give the image being exported. The default is its name in \fISRC_WIMFILE\fR. If specified, \fIDEST_IMAGE_NAME\fR must be either a name not already used in \fIDEST_WIMFILE\fR, or the empty string to leave the image unnamed. \fIDEST_IMAGE_NAME\fR cannot be specified if "all" images are being exported. .PP If specified, \fIDEST_IMAGE_DESC\fR is the description to give the image being exported. The default is its description in \fISRC_WIMFILE\fR. .PP \fBwimexport\fR supports exporting images from stand-alone WIMs as well as from split WIMs and delta WIMs. See \fBSPLIT WIMS\fR. .PP \fBwimexport\fR also supports exporting images from a non-pipable WIM into a pipable WIM or vice versa, or from a non-solid WIM into a solid WIM or vice versa. It can also export a pipable WIM directly to standard output if \fIDEST_WIMFILE\fR is specified as "-"; see \fB--pipable\fR. .PP .SH OPTIONS .TP 6 \fB--boot\fR Mark the exported image as the "bootable" image of the WIM; or, if multiple images are being exported, make the image that was the bootable image of the source WIM also the bootable image of the destination WIM. .TP \fB--check\fR Before exporting the image(s), verify the integrity of the source WIM, destination WIM, and any referenced WIMs whenever they have extra integrity information present. Also include extra integrity information in the destination WIM, even if it was not present before. .TP \fB--nocheck\fR Do not include extra integrity information in the destination WIM, even if it was present before. .TP \fB--include-integrity\fR Include extra integrity information in the destination WIM, i.e. like \fB--check\fR but don't do any verification beforehand. .TP \fB--compress\fR=\fITYPE\fR[:\fILEVEL\fR] Specify the compression type, and optionally the compression level for that compression type, for \fIDEST_WIMFILE\fR. Note that if \fIDEST_WIMFILE\fR already exists, then its compression type cannot be changed by this option. See the documentation for this option to \fBwimcapture\fR(1) for more details. .TP \fB--chunk-size\fR=\fISIZE\fR Set the WIM compression chunk size to \fISIZE\fR. See the documentation for this option to \fBwimcapture\fR(1) for more details. .TP \fB--recompress\fR Force all exported data to be recompressed, even if the destination WIM will use the same compression type as the source WIM. .TP \fB--solid\fR Create a "solid" archive that compresses multiple files together. This usually results in a significantly better compression ratio but has disadvantages such as reduced compatibility. See the documentation for this option to \fBwimcapture\fR(1) for more details. .TP \fB--solid-compress\fR=\fITYPE\fR[:\fILEVEL\fR] Like \fB--compress\fR, but set the compression type used in solid resources. See the documentation for this option to \fBwimcapture\fR(1) for more details. .TP \fB--solid-chunk-size\fR=\fISIZE\fR Like \fB--chunk-size\fR, but set the chunk size used in solid resources. See the documentation for this option to \fBwimcapture\fR(1) for more details. .TP \fB--threads\fR=\fINUM_THREADS\fR Number of threads to use for compressing data. Default: autodetect (number of processors). .TP \fB--rebuild\fR If exporting to an existing WIM, rebuild it rather than appending to it. Rebuilding is slower but will save some space that would otherwise be left as a hole in the WIM. Also see \fBwimoptimize\fR(1). .TP \fB--ref\fR="\fIGLOB\fR" File glob of additional WIMs or split WIM parts to reference resources from. See \fBSPLIT_WIMS\fR. This option can be specified multiple times. Note: \fIGLOB\fR is listed in quotes because it is interpreted by \fBwimexport\fR and may need to be quoted to protect against shell expansion. .TP \fB--pipable\fR Build or rebuild \fIDEST_WIMFILE\fR as a "pipable WIM" that can be applied fully sequentially, including from a pipe. See \fBwimcapture\fR(1) for more details about creating pipable WIMs. The default without this option is to make \fIDEST_WIMFILE\fR pipable only if it was "-" (standard output) or was an existing pipable WIM. .TP \fB--not-pipable\fR Rebuild \fIDEST_WIMFILE\fR as a normal, non-pipable WIM. This only useful if you are exporting image(s) to a pipable WIM but you want it rebuilt as non-pipable. .TP \fB--wimboot\fR Mark the destination image as WIMBoot-compatible. Also, if exporting to a new archive, set the compression type to that recommended for WIMBoot (currently, XPRESS with 4096 byte chunks). .TP \fB--unsafe-compact\fR Compact the existing destination WIM in-place and append any new data, eliminating "holes". This is efficient, but in general this option should \fInot\fR be used because a failed or interrupted compaction will corrupt the WIM archive. For more information, see the documentation for this option to \fBwimoptimize\fR(1). .SH SPLIT WIMS You may use \fBwimexport\fR to export images from (but not to) a split WIM. The \fISRC_WIMFILE\fR argument must specify the first part of the split WIM, while the additional parts of the split WIM must be specified in one or more \fB--ref\fR="\fIGLOB\fR" options. Since globbing is built into the \fB--ref\fR option, typically only one \fB--ref\fR option is necessary. For example, the names for the split WIM parts usually go something like: .PP .RS .nf mywim.swm mywim2.swm mywim3.swm mywim4.swm mywim5.swm .RE .PP To export the first image of this split WIM to a new or existing WIM file "other.wim", run: .PP .RS wimexport mywim.swm 1 other.wim --ref="mywim*.swm" .RE .SH NOTES \fIData consistency\fR: Except when using \fB--unsafe-compact\fR, it is safe to abort a \fBwimexport\fR command partway through. However, after doing this, it is recommended to run \fBwimoptimize\fR on the destination WIM to remove any data that was appended to the physical WIM file but not yet incorporated into the structure of the WIM, unless the WIM was being rebuilt (e.g. with \fB--rebuild\fR), in which case you should delete the temporary file left over. .PP \fIData deduplication\fR: The WIM format has built-in deduplication (also called "single instancing") of file contents. Therefore, when an image is exported, only the file contents not already present in the destination WIM will be physically copied. However, a new copy of the image's metadata resource, which describes the image's directory structure, will always be created. .PP \fIESD files\fR: \fBwimexport\fR supports solid-compressed WIMs, or "ESD" (.esd) files, except for encrypted ESDs, which must be decrypted first. The source and destination files of \fBwimexport\fR can be solid WIMs, non-solid WIMs, or a combination thereof. If the destination file does not exist, then by default it will be created as solid if the source was solid, or as non-solid if the source was non-solid. To override this, either specify \fB--solid\fR to create a solid WIM (.esd file), or specify \fB--compress\fR=\fILZX\fR to create a standard non-solid WIM (.wim file). .SH EXAMPLES Export the second image of 'boot.wim' to the new WIM file 'new.wim': .RS .PP wimexport boot.wim 2 new.wim .RE .PP The above example creates "new.wim" with the same compression type as "boot.wim". If you wish to change the compression type, specify \fB--compress\fR=\fITYPE\fR; for example: .RS .PP wimexport boot.wim 2 new.wim --compress=LZX .RE .PP Export "ESD to WIM" --- that is, solid WIM to non-solid WIM: .RS .PP wimexport install.esd all install.wim --compress=LZX .RE .PP Export "WIM to ESD" --- that is, non-solid WIM to solid WIM: .RS .PP wimexport install.wim all install.esd --solid .RE .PP .SH SEE ALSO .BR wimlib-imagex (1) .BR wiminfo (1) .BR wimoptimize (1) wimlib-1.13.1/doc/man1/wimsplit.10000644000175000017500000000311713464166215013346 00000000000000.TH WIMSPLIT "1" "May 2019" "wimlib 1.13.1" "User Commands" .SH NAME wimsplit \- Split a WIM archive into multiple parts .SH SYNOPSIS \fBwimsplit\fR \fIWIMFILE\fR \fISPLIT_WIM_PART_1\fR \fIPART_SIZE\fR [\fIOPTION...\fR] .SH DESCRIPTION \fBwimsplit\fR, or equivalently \fBwimlib-imagex split\fR, splits \fIWIMFILE\fR into parts with size at most \fIPART_SIZE\fR mebibytes (power-of-2 megabytes), with the first part having the name \fISPLIT_WIM_PART_1\fR and the other parts having names numbered in order of the parts. .PP \fBwimsplit\fR can split both non-pipable and pipable WIMs. .SH OPTIONS .TP 6 \fB--check\fR Before splitting the WIM, verify its integrity if it contains extra integrity information. Also include extra integrity information in each split WIM part, even if \fIWIMFILE\fR did not contain it. .TP \fB--include-integrity\fR Include extra integrity information in each split WIM part, i.e. like \fB--check\fR but don't also verify \fIWIMFILE\fR beforehand. .SH EXAMPLES Splits the WIM 'windows.wim' into 'windows.swm', 'windows2.swm', 'windows3.swm', etc. where each part is at most 100 MiB: .RS .PP wimsplit windows.wim windows.swm 100 .RE .SH LIMITATIONS It is possible for the size of the parts to exceed the \fIPART_SIZE\fR given. This is impossible to avoid because the WIM file format provides no way to divide a single file resource among multiple split WIM parts. So if you, for example, have a file inside the WIM that is 100 MiB compressed, then the split WIM will have at least one part that is 100 MiB in size to contain that file. .SH SEE ALSO .BR wimlib-imagex (1) .BR wimjoin (1) wimlib-1.13.1/doc/man1/wimmountrw.10000644000175000017500000000002413464166054013721 00000000000000.so man1/wimmount.1 wimlib-1.13.1/doc/man1/wimapply.10000644000175000017500000005054213464166215013344 00000000000000.TH WIMAPPLY "1" "May 2019" "wimlib 1.13.1" "User Commands" .SH NAME wimapply \- Apply a WIM image .SH SYNOPSIS \fBwimapply\fR \fIWIMFILE\fR [\fIIMAGE\fR] \fITARGET\fR [\fIOPTION\fR...] .SH DESCRIPTION \fBwimapply\fR, or equivalently \fBwimlib-imagex apply\fR, extracts ("applies") an image, or all images, from the Windows Imaging (WIM) archive \fIWIMFILE\fR. .PP \fIIMAGE\fR specifies the image in \fIWIMFILE\fR to extract. It may be the 1-based index of an image, the name of an image, or the keyword "all" to specify all images. It may be omitted if \fIWIMFILE\fR contains only one image. You can use \fBwiminfo\fR(1) to list the images contained in \fIWIMFILE\fR. .PP \fITARGET\fR specifies where to extract the image(s) to. If \fITARGET\fR is a directory, then the image(s) will be extracted to that directory as per \fBDIRECTORY EXTRACTION (UNIX)\fR or \fBDIRECTORY EXTRACTION (WINDOWS)\fR. If \fITARGET\fR does not exist, then a directory will be created there first. Alternatively, if \fITARGET\fR specifies a UNIX block device, then the image will be extracted to it as described in \fBNTFS VOLUME EXTRACTION (UNIX)\fR. .PP Note that \fBwimapply\fR is designed to extract, or "apply", full WIM images. If you instead want to extract only certain files or directories from a WIM image, use \fBwimextract\fR(1) instead. .PP If \fIIMAGE\fR is "all", then all images in \fIWIMFILE\fR will be extracted into subdirectories of \fITARGET\fR named after the images, falling back to the image index when an image has no name or an unusual name. This is not yet supported in \fBNTFS VOLUME EXTRACTION (UNIX)\fR mode. .PP If \fIWIMFILE\fR is "-", then the WIM is read from standard input rather than from disk. See \fBPIPABLE WIMS\fR for more information. .SH DIRECTORY EXTRACTION (UNIX) On UNIX-like systems, a WIM image may be extracted to a directory. This mode has the limitation that NTFS or Windows-specific metadata will not be extracted. Although some concepts such as hard links, symbolic links, last access timestamps, and last modification timestamps will be translated to their UNIX equivalents, other metadata will be lost (with warnings given). Notably, the following types of metadata will \fInot\fR be extracted in this mode: .IP \[bu] 4 Windows file attribute flags .IP \[bu] Windows security descriptors (e.g. file owners and DACLs) .IP \[bu] File creation timestamps .IP \[bu] Reparse points other than symbolic links and junction points .IP \[bu] Named data streams .IP \[bu] Short filenames (also known as 8.3 names or DOS names). .IP \[bu] Object IDs .PP These same limitations apply to \fBwimextract\fR. As such, this mode is most useful in situations where NTFS or Windows-specific metadata is unimportant, e.g. when wanting to extract specific files, or when doing file archiving only on UNIX-like systems, possibly in combination with \fB--unix-data\fR. When Windows-specific metadata is important, then either the \fBNTFS VOLUME EXTRACTION (UNIX)\fR mode should be used, or the Windows version of wimlib should be used (see \fBDIRECTORY EXTRACTION (WINDOWS)\fR). .SH NTFS VOLUME EXTRACTION (UNIX) On UNIX-like systems, \fITARGET\fR may also be specified as a block device (e.g. /dev/sda3) containing an unmounted NTFS volume. In this mode, \fBwimapply\fR uses libntfs-3g to apply the specified WIM image to the root directory of the NTFS volume. The target volume should be empty, e.g. newly created by \fBmkntfs\fR(8). In this mode, NTFS-specific and Windows-specific data and metadata will be extracted, including the following: .IP \[bu] 4 All data streams of all files except encrypted files, including the unnamed data stream as well as all named data streams. .IP \[bu] Reparse points, including symbolic links, junction points, and other reparse points. .IP \[bu] File and directory creation, access, and modification timestamps, using the native NTFS resolution of 100 nanoseconds. .IP \[bu] Windows security descriptors, including all components (owner, group, DACL, and SACL). .IP \[bu] Windows file attribute flags .IP \[bu] All names of all files, including names in the Win32 namespace, DOS namespace, Win32+DOS namespace, and POSIX namespace. This includes hard links. .IP \[bu] Object IDs. .PP However, encrypted files will not be extracted. .PP Restoring extended attributes (EAs) is also not yet supported in this mode. .PP Regardless, since almost all information from the WIM image is restored in this mode, it is possible (and fully supported) to restore an image of an actual Windows installation using \fBwimapply\fR on a UNIX-like system as an alternative to using \fBwimapply\fR or DISM on Windows. In the \fBEXAMPLES\fR section below, there is an example of applying an image from an "install.wim" file as may be found in the Windows installation media. .PP Note that to actually boot Windows (Vista or later) from an applied "install.wim" image, you also need to mark the partition as "bootable" and set up various boot files, such as \\BOOTMGR and \\BOOT\\BCD. The latter task is most easily accomplished by running bcdboot.exe from a live Windows system such as Windows PE, but there are other options as well. .PP Finally, note that this mode uses libntfs-3g directly, without going through the \fBntfs-3g\fR(8) driver. Hence, there is no special support for applying a WIM image to a directory on which an NTFS filesystem has been mounted using \fBntfs-3g\fR(8); you have to unmount it first. There is also no support for applying a WIM image to some subdirectory of the NTFS volume; you can only apply to the root directory. .SH DIRECTORY EXTRACTION (WINDOWS) On Windows, \fBwimapply\fR (and \fBwimextract\fR) natively support NTFS and Windows-specific metadata. For best results, the target directory should be located on an NTFS volume and the program should be run with Administrator privileges; however, non-NTFS filesystems and running without Administrator privileges are also supported, subject to limitations. .PP On Windows, \fBwimapply\fR tries to extract as much data and metadata as possible, including: .IP \[bu] 4 All data streams of all files. This includes the default file contents, as well as named data streams if supported by the target volume. .IP \[bu] Reparse points, including symbolic links, junction points, and other reparse points, if supported by the target volume. Restoring symlinks requires Administrator privileges. Also see \fB--rpfix\fR and \fB--norpfix\fR for details on how absolute symbolic links and junctions are extracted. .IP \[bu] File and directory creation, access, and modification timestamps, to the highest resolution supported by the target volume. .IP \[bu] Security descriptors, if supported by the filesystem and \fB--no-acls\fR is not specified. Note that this, in general, requires Administrator privileges, and may be only partially successful if the program is run without Administrator privileges (see \fB--strict-acls\fR). .IP \[bu] File attribute flags, including hidden, compressed, encrypted, sparse, etc, when supported by the filesystem. .IP \[bu] Short filenames (also known as 8.3 names or DOS names). .IP \[bu] Hard links, if supported by the target filesystem. .IP \[bu] Object IDs, if supported by the target filesystem. .IP \[bu] Extended attributes (EAs), if supported by the target filesystem. .PP Additional notes about extracting files on Windows: .IP \[bu] 4 \fBwimapply\fR will issue warnings if unable to extract the exact metadata and data of the WIM image due to limitations of the target filesystem. .IP \[bu] Since encrypted files (with FILE_ATTRIBUTE_ENCRYPTED) are not stored in plaintext in the WIM image, \fBwimapply\fR cannot restore encrypted files to filesystems not supporting encryption. Therefore, on such filesystems, encrypted files will not be extracted. Furthermore, even if encrypted files are restored to a filesystem that supports encryption, they will only be decryptable if the decryption key is available. .IP \[bu] Files with names that cannot be represented on Windows will not be extracted by default; see \fB--include-invalid-names\fR. .IP \[bu] Files with full paths over 260 characters (the so-called MAX_PATH) will be extracted, but beware that such files will be inaccessible to most Windows software and may not be able to be deleted easily. .IP \[bu] On Windows, unless the \fB--no-acls\fR option is specified, wimlib will attempt to restore files' security descriptors exactly as they are provided in the WIM image. Beware that typical Windows installations contain files whose security descriptors do not allow the Administrator to delete them. Therefore, such files will not be able to be deleted, or in some cases even read, after extracting, unless processed with a specialized program that knows to acquire the SE_RESTORE_NAME and/or SE_BACKUP_NAME privileges which allow overriding access control lists. This is not a bug in wimlib, which works as designed to correctly restore the data that was archived, but rather a problem with the access rights Windows uses on certain files. But if you just want the file data and don't care about security descriptors, use \fB--no-acls\fR to skip restoring all security descriptors. .IP \[bu] A similar caveat to the above applies to file attributes such as Readonly, Hidden, and System. By design, on Windows wimlib will restore such file attributes; therefore, extracted files may have those attributes. If this is not what you want, use the \fB--no-attributes\fR option. .SH SPLIT WIMS You may use \fBwimapply\fR to apply images from a split WIM, or \fBwimextract\fR to extract files from a split WIM. The \fIWIMFILE\fR argument must specify the first part of the split WIM, while the additional parts of the split WIM must be specified in one or more \fB--ref\fR="\fIGLOB\fR" options. Since globbing is built into the \fB--ref\fR option, typically only one \fB--ref\fR option is necessary. For example, the names for the split WIM parts usually go something like: .RS .PP .nf mywim.swm mywim2.swm mywim3.swm mywim4.swm mywim5.swm .RE .fi .PP To apply the first image of this split WIM to the directory "dir", run: .PP .RS wimapply mywim.swm 1 dir --ref="mywim*.swm" .RE .PP .SH PIPABLE WIMS \fBwimapply\fR also supports applying a WIM from a nonseekable file, such as a pipe, provided that the WIM was captured in the wimlib-specific pipable format using \fB--pipable\fR (see \fBwimcapture\fR(1)). To use standard input as the WIM, specify "-" as \fIWIMFILE\fR. A possible use of this feature is to apply a WIM image being streamed from the network. For example, to apply the first image from a WIM file available on a HTTP server to an NTFS volume on /dev/sda1, run something like: .PP .RS wget -O - http://myserver/mywim.wim | wimapply - 1 /dev/sda1 .RE .PP Pipable WIMs may also be split into multiple parts, just like normal WIMs. To apply a split pipable WIM from a pipe, the parts must be concatenated and all written to the pipe. The first part must be sent first, but the remaining parts may be sent in any order. .SH OPTIONS .TP 6 \fB--check\fR Before applying the image, verify the integrity of \fIWIMFILE\fR if it has extra integrity information. .TP \fB--ref\fR="\fIGLOB\fR" File glob of additional WIMs or split WIM parts to reference resources from. See \fBSPLIT_WIMS\fR. This option can be specified multiple times. Note: \fIGLOB\fR is listed in quotes because it is interpreted by \fBwimapply\fR and may need to be quoted to protect against shell expansion. .TP \fB--rpfix\fR, \fB--norpfix\fR Set whether to fix targets of absolute symbolic links (reparse points in Windows terminology) or not. When enabled (\fB--rpfix\fR), extracted absolute symbolic links that are marked in the WIM image as being fixed are assumed to have absolute targets relative to the image root, and therefore \fBwimapply\fR prepends the absolute path to the extraction target directory to their targets. The intention is that you can apply an image containing absolute symbolic links and still have them be valid after it has been applied to any location. .IP "" The default behavior is \fB--rpfix\fR if any images in \fIWIMFILE\fR have been captured with reparse-point fixups done. Otherwise, it is \fB--norpfix\fR. .IP "" Reparse point fixups are never done in the NTFS volume extraction mode on UNIX-like systems. .TP \fB--unix-data\fR (UNIX-like systems only) Restore UNIX-specific metadata and special files that were captured by \fBwimcapture\fR with the \fB--unix-data\fR option. This includes: standard UNIX file permissions (owner, group, and mode); device nodes, named pipes, and sockets; and extended attributes (Linux-only). .TP \fB--no-acls\fR Do not restore security descriptors on extracted files and directories. .TP \fB--strict-acls\fR Fail immediately if the full security descriptor of any file or directory cannot be set exactly as specified in the WIM file. If this option is not specified, when \fBwimapply\fR on Windows does not have permission to set a security descriptor on an extracted file, it falls back to setting it only partially (e.g. with SACL omitted), and in the worst case omits it entirely. However, this should only be a problem when running \fBwimapply\fR without Administrator rights. Also, on UNIX-like systems, this flag can also be combined with \fB--unix-data\fR to cause \fBwimapply\fR to issue an error if UNIX permissions are unable to be applied to an extracted file. .TP \fB--no-attributes\fR Do not restore Windows file attributes such as readonly, hidden, etc. .TP \fB--include-invalid-names\fR Extract files and directories with invalid names by replacing characters and appending a suffix rather than ignoring them. Exactly what is considered an "invalid" name is platform-dependent. .IP "" On POSIX-compliant systems, filenames are case-sensitive and may contain any byte except '\\0' and \'/', so on a POSIX-compliant system this option will only have an effect in the unlikely case that the WIM image for some reason has a filename containing one of these characters. .IP "" On Windows, filenames are case-insensitive(*), cannot include control characters, and cannot include the characters '/', \'\\0', '\\', ':', '*', '?', \'"', '<', '>', or '|'. Ordinarily, files in WIM images should meet these conditions as well. However, it is not guaranteed, and in particular a WIM image captured with \fBwimcapture\fR on a POSIX-compliant system could contain such files. By default, invalid names will be ignored, and if there are multiple names differing only in case, one will be chosen to extract arbitrarily; however, with \fB--include-invalid-names\fR, all names will be sanitized and extracted in some form. .IP "" (*) Unless the ObCaseInsensitive setting has been set to 0 in the Windows registry, in which case certain software, including the Windows version of \fBwimapply\fR, will honor case-sensitive filenames on NTFS and other compatible filesystems. .TP \fB--wimboot\fR Windows only: Instead of extracting the files themselves, extract "pointer files" back to the WIM archive(s). This can result in significant space savings. However, it comes at several potential costs, such as not being able to delete the WIM archive(s) and possibly having slower access to files. See Microsoft's documentation for "WIMBoot" for more information. .IP "" If it exists, the [PrepopulateList] section of the file \\Windows\\System32\\WimBootCompress.ini in the WIM image will be read. Files matching any of these patterns will be extracted normally, not as WIMBoot "pointer files". This is helpful for certain files that Windows needs to read early in the boot process. .IP "" This option only works when the program is run as an Administrator and the target volume is NTFS or another filesystem that supports reparse points. .IP "" In addition, this option works best when running on Windows 8.1 Update 1 or later, since that is the first version of Windows that contains the Windows Overlay Filesystem filter driver ("WOF"). If the WOF driver is detected, wimlib will create the WIMBoot "pointer files" using documented ioctls provided by WOF. .IP "" Otherwise, if the WOF driver is not detected, wimlib will create the reparse points and edit the file "\\System Volume Information\\WimOverlay.dat" on the target volume manually. This is potentially subject to problems, since although the code works in certain tested cases, neither of these data formats is actually documented by Microsoft. Before overwriting this file, wimlib will save the previous version in "\\System Volume Information\\WimOverlay.wimlib_backup", which you potentially could restore if you needed to. .IP "" You actually can still do a \fB--wimboot\fR extraction even if the WIM image is not marked as "WIMBoot-compatible". This option causes the extracted files to be set as "externally backed" by the WIM file. Microsoft's driver which implements this "external backing" functionality seemingly does not care whether the image(s) in the WIM are really marked as WIMBoot-compatible. Therefore, the "WIMBoot-compatible" tag ( in the XML data) seems to be a marker for intent only. In addition, the Microsoft driver can externally back files from WIM files that use XPRESS chunks of size 8192, 16384, and 32768, or LZX chunks of size 32768, in addition to the default XPRESS chunks of size 4096 that are created when \fBwimcapture\fR is run with the \fB--wimboot\fR option. .TP \fB--compact\fR=\fIFORMAT\fR Windows-only: compress the extracted files using System Compression, when possible. This only works on either Windows 10 or later, or on an older Windows to which Microsoft's wofadk.sys driver has been added. Several different compression formats may be used with System Compression, and one must be specified as \fIFORMAT\fR. The choices are: xpress4k, xpress8k, xpress16k, and lzx. .IP "" Exclusions are handled in the same way as with the \fB--wimboot\fR option. That is: if it exists, the [PrepopulateList] section of the file \\Windows\\System32\\WimBootCompress.ini in the WIM image will be read, and files matching any of the patterns in this section will not be compressed. In addition, wimlib has a hardcoded list of files for which it knows, for compatibility with the Windows bootloader, to override the requested compression format. .SH NOTES \fIData integrity\fR: WIM files include checksums of file data. To detect accidental (non-malicious) data corruption, wimlib calculates the checksum of every file it extracts and issues an error if it does not have the expected value. (This default behavior seems equivalent to the \fB/verify\fR option of ImageX.) In addition, a WIM file can include an integrity table (extra checksums) over the raw data of the entire WIM file. For performance reasons wimlib does not check the integrity table by default, but the \fB--check\fR option can be passed to make it do so. .PP \fIESD files\fR: wimlib can extract files from solid-compressed WIMs, or "ESD" (.esd) files, just like from normal WIM (.wim) files. However, Microsoft sometimes distributes ESD files with encrypted segments; wimlib cannot extract such files until they are first decrypted. .PP \fISecurity\fR: wimlib has been carefully written to validate all input and is believed to be secure against some types of attacks which often plague other file archiving programs, e.g. directory traversal attacks (which, as it happens, Microsoft's WIM software is vulnerable to). Important parts of wimlib, e.g. the decompressors, have also been fuzz tested. However, wimlib is not currently designed to protect against some types of denial-of-service (DOS) attacks, e.g. memory exhaustion or "zip bombs". .SH EXAMPLES Extract the first image from the Windows PE WIM on the Windows installation media to the directory "boot": .RS .PP wimapply /mnt/windows/sources/boot.wim 1 boot .RE .PP On Windows, apply an image of an entire volume, for example from "install.wim" which can be found on the Windows installation media: .RS .PP wimapply install.wim 1 E:\\ .RE .PP Same as above, but running on a UNIX-like system where the corresponding partition is /dev/sda2: .RS .PP wimapply install.wim 1 /dev/sda2 .RE .PP Note that before running either of the above commands, an NTFS filesystem may need to be created on the partition, for example with format.exe on Windows or \fBmkntfs\fR(8) on UNIX-like systems. For example, on UNIX you might run: .RS .PP mkntfs /dev/sda2 && wimapply install.wim 1 /dev/sda2 .RE .PP (Of course don't do that if you don't want to destroy all existing data on the partition!) .PP See \fBSPLIT WIMS\fR and \fBPIPABLE WIMS\fR for examples of applying split and pipable WIMs, respectively. .SH SEE ALSO .BR wimlib-imagex (1) .BR wimcapture (1) .BR wimextract (1) .BR wiminfo (1) wimlib-1.13.1/doc/man1/wimextract.10000644000175000017500000002056113464166215013667 00000000000000.TH WIMEXTRACT "1" "May 2019" "wimlib 1.13.1" "User Commands" .SH NAME wimextract \- Extract files from a WIM image .SH SYNOPSIS \fBwimextract\fR \fIWIMFILE\fR \fIIMAGE\fR [(\fIPATH\fR | @\fILISTFILE\fR)...] [\fIOPTION\fR...] .SH DESCRIPTION \fBwimextract\fR, or equivalently \fBwimlib-imagex extract\fR, extracts one or more files or directory trees from the specified \fIIMAGE\fR contained in the Windows Imaging (WIM) archive \fIWIMFILE\fR. .PP \fBwimextract\fR is intended for extracting only a subset of a WIM image. If you want to extract or "apply" a full WIM image to a directory or NTFS volume, use \fBwimapply\fR(1) instead. .PP \fIIMAGE\fR specifies the image in \fIWIMFILE\fR from which to extract the files or directory trees. It may be the 1-based index of an image or the name of an image. It may be omitted if \fIWIMFILE\fR contains only one image. You can use \fBwiminfo\fR(1) to list the images contained in \fIWIMFILE\fR. .PP If no additional arguments are given, the entire WIM image is extracted. Otherwise, each additional argument is interpreted as a \fIPATH\fR if it does not begin with the '@' character, or a \fILISTFILE\fR if it does. Each \fIPATH\fR specifies a file or directory tree within the WIM image to extract, whereas each \fILISTFILE\fR specifies a file that itself contains a list of paths to extract. If a \fILISTFILE\fR is "-" (i.e. the whole argument is "@-"), then the listfile is read from standard input. See \fBPATHS AND LISTFILES\fR for more details. .PP By default, files and directories are extracted to the current directory. Use \fB--dest-dir\fR to select a different destination directory. Alternatively, use \fB--to-stdout\fR to extract a file to standard output to pipe into another program. .PP A file or directory extracted from a \fIPATH\fR argument is by default extracted directly into the destination directory, whereas a file or directory extracted from a \fILISTFILE\fR argument is by default extracted into the destination directory in such a way that the archive's directory structure is preserved. Use \fB--preserve-dir-structure\fR to always get the latter behavior. .PP \fBwimextract\fR supports extracting files and directory trees from stand-alone WIMs as well as split WIMs. See \fBSPLIT WIMS\fR. .SH PATHS AND LISTFILES Each path, including those on the command line and those in listfiles, must be specified as an absolute path starting from the root of the WIM image, like those output by \fBwimdir\fR(1). However, path separators may be either forward or backward slashes, and the leading slash is optional. .PP On Windows, by default paths are treated case-insensitively, whereas on UNIX-like systems, by default paths are treated case-sensitively. In either case, the default behavior may be overridden through the \fBWIMLIB_IMAGEX_IGNORE_CASE\fR environmental variable, as documented in \fBwimlib-imagex\fR(1). .PP By default, each path may contain the wildcard characters '?' and '*'. The '?' character matches any non-path-separator character, whereas the '*' character matches zero or more non-path-separator characters. Consequently, a single wildcard path, or "glob", may expand to multiple actual files or directories. Use the \fB--no-globs\fR option to disable wildcard matching and search for each path literally. .PP Each \fILISTFILE\fR must be a text file (UTF-8 or UTF-16LE encoded; plain ASCII is also fine) that contains a list of paths to extract, one per line. Wildcard characters are allowed by default. The following demonstrates an example listfile: .PP .RS .nf ; This is a comment (begins with semicolon) # This is also a comment (begins with number sign) /Users /Windows/explorer.exe /Windows/System32/en-US/* ; Both forward and backslashes are valid. ; It's not necessary to quote paths containing internal spaces. \\Program Files\\A* ; Leading and trailing whitespace is ignored \\Windows\\notepad* .SH SPLIT WIMS You may use \fBwimextract\fR to extract files or directory trees from a split WIM. This uses the \fB--refs\fR="\fIGLOB\fR" option in the same way as in other commands such as \fBwimapply\fR. See \fBwimapply\fR(1) for more details. .SH OPTIONS .TP 6 \fB--check\fR Before extracting the files, verify the integrity of \fIWIMFILE\fR if it contains extra integrity information. .TP \fB--ref\fR="\fIGLOB\fR" File glob of additional WIMs or split WIM parts to reference resources from. See \fBSPLIT_WIMS\fR. Note: \fIGLOB\fR is listed in quotes because it is interpreted by \fBwimextract\fR and may need to be quoted to protect against shell expansion. .TP \fB--dest-dir\fR=\fIDIR\fR Extract the files and directories to the directory \fIDIR\fR instead of to the current working directory. .TP \fB--to-stdout\fR Extract the files to standard output instead of to the filesystem. This can only be provided if all the specified paths are to regular files (not directories or reparse points). If present, named data streams are not extracted. .TP \fB--unix-data\fR See the documentation for this option to \fBwimapply\fR(1). .TP \fB--no-acls\fR See the documentation for this option to \fBwimapply\fR(1). .TP \fB--strict-acls\fR See the documentation for this option to \fBwimapply\fR(1). .TP \fB--no-attributes\fR See the documentation for this option to \fBwimapply\fR(1). .TP \fB--include-invalid-names\fR See the documentation for this option to \fBwimapply\fR(1). .TP \fB--no-globs\fR Do not recognize wildcard characters in paths. Each path will be searched for literally. In addition, if case insensitivity is enabled, do not allow a single path to match multiple files with the same case-insensitive name but different case-sensitive names. .IP \fB--no-wildcards\fR is also accepted as an alias for this option. .TP \fB--nullglob\fR If a glob does not match any files, ignore it and print a warning instead of failing with an error. In other words, this option allows a glob to successfully match zero files. .IP This option also affects paths that do not contain wildcard characters, since such paths are still considered globs unless \fB--no-globs\fR is enabled. If case-insensitivity is enabled, such a glob could match multiple files with the same case-insensitive name but different case-sensitive names, whereas a non-glob path (with \fB--no-globs\fR) can match at most one file. .TP \fB--preserve-dir-structure\fR When extracting paths, preserve the archive directory structure instead of extracting the file or directory tree named by each path directly to the destination directory. Note: \fB--preserve-dir-structure\fR is already the default behavior for paths in listfiles, but not paths directly specified on the command line. .TP \fB--wimboot\fR See the documentation for this option to \fBwimapply\fR(1). .TP \fB--compact\fR=\fIFORMAT\fR See the documentation for this option to \fBwimapply\fR(1). .SH NOTES See \fBwimapply\fR(1) for information about what data and metadata are extracted on UNIX-like systems versus on Windows. .PP Reparse-point fixups (a.k.a. changing absolute symbolic links and junctions to point within the extraction location) are never done by \fBwimextract\fR. Use \fBwimapply\fR if you want this behavior. .PP Unlike \fBwimapply\fR, \fBwimextract\fR does not support extracting files directly to an NTFS volume using libntfs-3g. .SH EXAMPLES Extract a file from the first image in "boot.wim" to the current directory: .RS .PP wimextract boot.wim 1 /Windows/System32/notepad.exe .RE .PP Extract a file from the first image in "boot.wim" to standard output: .RS .PP wimextract boot.wim 1 /Windows/System32/notepad.exe --to-stdout .RE .PP Extract a file from the first image in "boot.wim" to the specified directory: .RS .PP wimextract boot.wim 1 /Windows/System32/notepad.exe \\ .br .RS --dest-dir=somedir .RE .RE .PP Extract the "sources" directory from the first image in "boot.wim" to the current directory: .RS .PP wimextract boot.wim 1 /sources .RE .PP Extract multiple files and directories in one command: .RS .PP wimextract boot.wim 1 /Windows/Fonts \\ .br .RS /sources /Windows/System32/cmd.exe .RE .RE .PP Extract many files to the current directory using a wildcard pattern: .RS .PP wimextract install.wim 1 "/Windows/Fonts/*.ttf" .RE .PP Extract files using a list file: .RS .PP wimextract install.wim 1 @files.txt .RE .PP ... where files.txt could be something like: .PP .RS .RS .nf Windows\\System32\\*.* Windows\\System32\\??-??\\*.* Windows\\System32\\en-US\\*.* .RE .RE .fi .SH SEE ALSO .BR wimlib-imagex (1) .BR wimapply (1) .BR wimdir (1) .BR wiminfo (1) wimlib-1.13.1/doc/man1/mkwinpeimg.10000644000175000017500000001274213464166215013651 00000000000000.TH MKWINPEIMG "1" "May 2019" "wimlib 1.13.1" "User Commands" .SH NAME mkwinpeimg \- Make a customized bootable image of Windows PE .SH SYNOPSIS .B mkwinpeimg [\fIOPTIONS\fR] \fIIMAGE\fR .SH DESCRIPTION \fBmkwinpeimg\fR is able to make a bootable image of Windows PE by taking files from a mounted Windows installation ISO image (Vista or later) or the mounted ISO image for the Windows Automated Installation Kit (WAIK) or the WAIK supplement. The \fB--windows-dir\fR and \fB--waik-dir\fR options are used to specify the locations of these mounted ISOs. You only need one or the other. The files that \fBmkwinpeimg\fR will retrieve are \fIboot.wim\fR, \fIbootmgr\fR, \fIboot.sdi\fR, and \fIbcd\fR. If making an ISO image, the file \fIetfsboot.com\fR is also retrieved. Microsoft owns the rights to these files and they are not distributed with wimlib. .PP \fBmkwinpeimg\fR can make two types of bootable images. The default is to make a bootable disk image. The image is not partitioned and is formatted into a FAT filesystem. \fBsyslinux\fR(1) is required to make this type of image, as it is used to chainload \fIbootmgr\fR. Also, \fBmtools\fR(1) is required so that the FAT filesystem can be created without root privileges. .PP The other type of bootable image that \fBmkwinpeimg\fR can make is a bootable ISO image. To make this type of image, give the \fB--iso\fR option. \fBmkisofs\fR(1) is required to make this type of image. In \fB--iso\fR mode, you can specify \fIIMAGE\fR as "-" to write the ISO image to standard output. .PP If you make a disk image, you could put it on a USB drive, and if you make an ISO image, you could put it on a CD. In addition, both types of images can be loaded by the SYSLINUX or PXELINUX bootloaders using the MEMDISK module. .PP Windows PE itself is contained in the \fIboot.wim\fR file. \fBmkwinpeimg\fR can modify this file before embedding it in a bootable image. The most useful modification is to specify an executable or batch file for Windows PE to execute as soon as it starts up. Use the \fB--start-script\fR \fIFILE\fR option to specify such a file. You may also add arbitrary files to \fIboot.wim\fR by putting them in a directory, then specifying the \fB--overlay\fR \fIDIR\fR option. However, for more extensive modifications, consider modifying the \fIboot.wim\fR file separately using \fBwimmountrw\fR(1) or \fBwimupdate\fR(1), then providing it to \fBmkwinpeimg\fR using the \fB--wim\fR option. .PP \fBmkwinpeimg\fR can also make only a modified \fIboot.wim\fR, rather than a bootable ISO or disk image, if the \fB--only-wim\fR option is given. .SH OPTIONS .TP 6 \fB\-i\fR, \fB\-\-iso\fR Make an ISO image instead of a disk image. .TP \fB\-o\fR, \fB\-\-only-wim\fR Make neither a disk image nor an ISO image; instead, only make a modified \fIboot.wim\fR file. .TP \fB\-W\fR, \fB\-\-windows\-dir\fR=\fIDIR\fR Use DIR as the location of the mounted Windows installation ISO image. If neither \fB\-\-windows\-dir\fR nor \fB\-\-waik\-dir\fR is specified, then the script looks for the mounted ISO in the following default locations: /mnt/windows, /mnt/windows7, /mnt/windows8, and /mnt/windows10. .TP \fB\-A\fR, \fB\-\-waik\-dir\fR=\fIDIR\fR Get the boot files and boot.wim from the ISO of the Windows Automated Installation Kit mounted on DIR instead of from a Windows installation ISO. This also works if the mounted ISO is for the WAIK supplement rather than the WAIK itself. .TP \fB\-s\fR, \fB\-\-start\-script\fR=\fIFILE\fR Add FILE to the root directory of Windows PE image and adjust \eWindows\eSystem32\ewinpeshl.ini to execute FILE when Windows PE starts up. .TP \fB\-w\fR, \fB\-\-wim\fR=\fIWIM\fR Use WIM as the "boot.wim" file. This defaults to the appropriate WIM file from the Windows ISO, WAIK, or WAIK supplement. .TP \fB\-O\fR, \fB\-\-overlay\fR=\fIDIR\fR Adds all the files in DIR to the Windows PE image. .TP \fB\-t\fR, \fB\-\-tmp\-dir\fR=\fIDIR\fR Use DIR for temporary files. Defaults to a directory made using "mktemp -d". .TP \fB\-a\fR, \fB\-\-arch\fR=\fIARCH\fR Use the Windows PE version from the Windows Automated Installation Kit (or the WAIK supplement) that has the CPU architecture \fIARCH\fR. Only valid with \fB\-\-waik\-dir\fR. Possible values: "x86" or "amd64". Default is "x86". Note: the WAIK also contains a Windows PE image for the ia64 (Itanium) architecture; however, this is not supported by this script. .TP \fB\-h\fR, \fB\-\-help\fR Display help. .TP \fB\-v\fR, \fB\-\-version\fR Show version information. .SH EXAMPLES Create a bootable disk image of Windows PE from the Windows (Vista or later) installation media mounted on /media/windows: .RS .PP mkwinpeimg --windows-dir=/media/windows winpe.img .RE .PP Create a bootable ISO of Windows PE from the WAIK (or WAIK supplement) mounted on /media/waik, and add all the files in "winpe_overlay" to Windows PE's filesystem: .RS .PP mkwinpeimg --iso --waik-dir=/media/waik --overlay=winpe_overlay winpe.iso .RE .PP Create a bootable image of Windows PE from the Windows installation media mounted on /media/windows, add and make it execute "install.cmd" when it starts up. In this example the image is created in the root directory of the TFTP server for network booting. .RS .PP mkwinpeimg --start-script=install.cmd --windows-dir=/media/windows /var/tftpboot/winpe.img .RE .PP .SH NOTES Microsoft's licenses may limit the things that Windows PE can be used for, and they may limit your rights to redistribute customized versions of Windows PE. .SH REPORTING BUGS Report bugs to ebiggers3@gmail.com. .SH SEE ALSO .BR wimlib-imagex (1) wimlib-1.13.1/doc/Doxyfile.in0000644000175000017500000000131412260745400012660 00000000000000PROJECT_NAME = wimlib OUTPUT_DIRECTORY = . EXTRACT_ALL = YES EXTRACT_LOCAL_CLASSES = YES HIDE_UNDOC_MEMBERS = YES SHOW_INCLUDE_FILES = YES JAVADOC_AUTOBRIEF = YES OPTIMIZE_OUTPUT_FOR_C = YES SHOW_USED_FILES = YES INPUT = @top_srcdir@/include/wimlib.h FULL_PATH_NAMES = NO OPTIMIZE_FOR_C = YES GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_TIMESTAMP = NO GENERATE_HTMLHELP = NO GENERATE_LATEX = NO GENERATE_RTF = NO GENERATE_MAN = NO GENERATE_XML = NO GENERATE_AUTOGEN_DEF = NO CLASS_DIAGRAMS = NO SORT_MEMBER_DOCS = NO wimlib-1.13.1/include/0000755000175000017500000000000013464166632011517 500000000000000wimlib-1.13.1/include/wimlib/0000755000175000017500000000000013464166632013002 500000000000000wimlib-1.13.1/include/wimlib/sha1.h0000644000175000017500000000431713160354224013721 00000000000000/* * sha1.h * * The following copying information applies to this specific source code file: * * Written in 2013-2015 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . */ #ifndef _WIMLIB_SHA1_H #define _WIMLIB_SHA1_H #include #include "wimlib/types.h" #include "wimlib/util.h" #define SHA1_HASH_SIZE 20 extern const u8 zero_hash[SHA1_HASH_SIZE]; extern void sprint_hash(const u8 hash[SHA1_HASH_SIZE], tchar strbuf[SHA1_HASH_SIZE * 2 + 1]); static inline void copy_hash(u8 dest[SHA1_HASH_SIZE], const u8 src[SHA1_HASH_SIZE]) { memcpy(dest, src, SHA1_HASH_SIZE); } static inline int hashes_cmp(const u8 h1[SHA1_HASH_SIZE], const u8 h2[SHA1_HASH_SIZE]) { return memcmp(h1, h2, SHA1_HASH_SIZE); } static inline bool hashes_equal(const u8 h1[SHA1_HASH_SIZE], const u8 h2[SHA1_HASH_SIZE]) { return !hashes_cmp(h1, h2); } static inline bool is_zero_hash(const u8 *hash) { return (hash == zero_hash || hashes_equal(hash, zero_hash)); } #ifdef WITH_LIBCRYPTO #include #define sha1_init SHA1_Init #define sha1_update SHA1_Update #define sha1_final SHA1_Final static inline void sha1_buffer(const void *buffer, size_t len, u8 hash[SHA1_HASH_SIZE]) { SHA1(buffer, len, hash); } #else /* WITH_LIBCRYPTO */ typedef struct { u64 bytecount; u32 state[5]; u8 buffer[64]; } SHA_CTX; extern void sha1_init(SHA_CTX *ctx); extern void sha1_update(SHA_CTX *ctx, const void *data, size_t len); extern void sha1_final(u8 hash[SHA1_HASH_SIZE], SHA_CTX *ctx); extern void sha1_buffer(const void *buffer, size_t len, u8 hash[SHA1_HASH_SIZE]); #endif /* !WITH_LIBCRYPTO */ #endif /* _WIMLIB_SHA1_H */ wimlib-1.13.1/include/wimlib/wim.h0000644000175000017500000001757213160354224013670 00000000000000/* * wim.h - WIMStruct definition and helper functions */ #ifndef _WIMLIB_WIM_H #define _WIMLIB_WIM_H #include "wimlib.h" #include "wimlib/file_io.h" #include "wimlib/header.h" #include "wimlib/list.h" struct wim_image_metadata; struct wim_xml_info; struct blob_table; /* * WIMStruct - represents a WIM, or a part of a non-standalone WIM * * Note 1: there are three ways in which a WIMStruct can be created: * * 1. open an on-disk WIM file * 2. start to extract a pipable WIM from a file descriptor * 3. create a new WIMStruct directly * * For (1) and (2), the WIMStruct has a backing file; for (3) it does not. For * (1), the backing file is a real "on-disk" file from the filesystem, whereas * for (2) the backing file is a file descriptor which may be a pipe. * * Note 2: although this is the top-level data structure in wimlib, there do * exist cases in which a WIMStruct is not standalone: * - blobs have been referenced from another WIMStruct * - an image has been imported into this WIMStruct from another * (as this references the metadata rather than copies it) * * Note 3: It is unsafe for multiple threads to operate on the same WIMStruct at * the same time. This extends to references to other WIMStructs as noted * above. But besides this, it is safe to operate on *different* WIMStructs in * different threads concurrently. */ struct WIMStruct { /* Information from the header of the WIM file. * * This is also maintained for a WIMStruct not backed by a file, but in * that case the 'reshdr' fields are left zeroed. */ struct wim_header hdr; /* If the library is currently writing this WIMStruct out to a file, * then this is the header being created for that file. */ struct wim_header out_hdr; /* Array of image metadata, one for each image in the WIM (array length * hdr.image_count). Or, this will be NULL if this WIM does not contain * metadata, which implies that this WIMStruct either represents part of * a non-standalone WIM, or represents a standalone WIM that, oddly * enough, actually contains 0 images. */ struct wim_image_metadata **image_metadata; /* Information from the XML data of the WIM file. This information is * also maintained for a WIMStruct not backed by a file. */ struct wim_xml_info *xml_info; /* The blob table for this WIMStruct. If this WIMStruct has a backing * file, then this table will index the blobs contained in that file. * In addition, this table may index blobs that were added by updates or * referenced from other WIMStructs. */ struct blob_table *blob_table; /* The number of references to this WIMStruct. This is equal to the * number of resource descriptors that reference this WIMStruct, plus 1 * if wimlib_free() still needs to be called. */ ssize_t refcnt; /* * The 1-based index of the currently selected image in this WIMStruct, * or WIMLIB_NO_IMAGE if no image is currently selected. * * The metadata for the current image is image_metadata[current_image - * 1]. Since we load image metadata lazily, only the metadata for the * current image is guaranteed to actually be present in memory. */ int current_image; /* The absolute path to the on-disk file backing this WIMStruct, or NULL * if this WIMStruct is not backed by an on-disk file. */ tchar *filename; /* If this WIMStruct has a backing file, then this is a file descriptor * open to that file with read access. Otherwise, this field is invalid * (!filedes_valid(&in_fd)). */ struct filedes in_fd; /* If the library is currently writing this WIMStruct out to a file, * then this is a file descriptor open to that file with write access. * Otherwise, this field is invalid (!filedes_valid(&out_fd)). */ struct filedes out_fd; /* * This is the cached decompressor for this WIM file, or NULL if no * decompressor is cached yet. Normally, all the compressed data in a * WIM file has the same compression type and chunk size, so the same * decompressor can be used for all data --- and that decompressor will * be cached here. However, if we do encounter any data with a * different compression type or chunk size (this is possible in solid * resources), then this cached decompressor will be replaced with a new * one. */ struct wimlib_decompressor *decompressor; u8 decompressor_ctype; u32 decompressor_max_block_size; /* Temporary field; use sparingly */ void *private; /* 1 if any images have been deleted from this WIMStruct, otherwise 0 */ u8 image_deletion_occurred : 1; /* 1 if the WIM file has been locked for appending, otherwise 0 */ u8 locked_for_append : 1; /* 1 if the WIM file is currently being compacted by wimlib_overwrite() * with WIMLIB_WRITE_FLAG_UNSAFE_COMPACT */ u8 being_compacted : 1; /* If this WIM is backed by a file, then this is the compression type * for non-solid resources in that file. */ u8 compression_type; /* Overridden compression type for wimlib_overwrite() or wimlib_write(). * Can be changed by wimlib_set_output_compression_type(); otherwise is * the same as compression_type. */ u8 out_compression_type; /* Compression type for writing solid resources; can be set with * wimlib_set_output_pack_compression_type(). */ u8 out_solid_compression_type; /* If this WIM is backed by a file, then this is the compression chunk * size for non-solid resources in that file. */ u32 chunk_size; /* Overridden chunk size for wimlib_overwrite() or wimlib_write(). Can * be changed by wimlib_set_output_chunk_size(); otherwise is the same * as chunk_size. */ u32 out_chunk_size; /* Chunk size for writing solid resources; can be set with * wimlib_set_output_pack_chunk_size(). */ u32 out_solid_chunk_size; /* Currently registered progress function for this WIMStruct, or NULL if * no progress function is currently registered for this WIMStruct. */ wimlib_progress_func_t progfunc; void *progctx; }; /* * Return true if and only if the WIM contains image metadata (actual directory * trees, not just a collection of blobs and their checksums). * * See the description of the 'image_metadata' field. Note that we return true * when the image count is 0 because it could be a WIM with 0 images. It's only * when the WIM does not contain the metadata described by its image count that * we return false. */ static inline bool wim_has_metadata(const WIMStruct *wim) { return (wim->image_metadata != NULL || wim->hdr.image_count == 0); } /* Return true if and only if the WIM has an integrity table. * * If the WIM is not backed by a file, then this always returns false. */ static inline bool wim_has_integrity_table(const WIMStruct *wim) { return (wim->hdr.integrity_table_reshdr.offset_in_wim != 0); } /* Return true if and only if the WIM is in pipable format. * * If the WIM is not backed by a file, then this always returns false. */ static inline bool wim_is_pipable(const WIMStruct *wim) { return (wim->hdr.magic == PWM_MAGIC); } extern void wim_decrement_refcnt(WIMStruct *wim); extern bool wim_has_solid_resources(const WIMStruct *wim); extern int read_wim_header(WIMStruct *wim, struct wim_header *hdr); extern int write_wim_header(const struct wim_header *hdr, struct filedes *out_fd, off_t offset); extern int write_wim_header_flags(u32 hdr_flags, struct filedes *out_fd); extern int select_wim_image(WIMStruct *wim, int image); extern void deselect_current_wim_image(WIMStruct *wim); extern int for_image(WIMStruct *wim, int image, int (*visitor)(WIMStruct *)); extern int wim_checksum_unhashed_blobs(WIMStruct *wim); extern int delete_wim_image(WIMStruct *wim, int image); /* Internal open flags (pass to open_wim_as_WIMStruct(), not wimlib_open_wim()) */ #define WIMLIB_OPEN_FLAG_FROM_PIPE 0x80000000 extern int open_wim_as_WIMStruct(const void *wim_filename_or_fd, int open_flags, WIMStruct **wim_ret, wimlib_progress_func_t progfunc, void *progctx); extern int can_modify_wim(WIMStruct *wim); #endif /* _WIMLIB_WIM_H */ wimlib-1.13.1/include/wimlib/pathlist.h0000644000175000017500000000036113030246726014713 00000000000000#ifndef _WIMLIB_PATHLIST_H #define _WIMLIB_PATHLIST_H #include "wimlib/types.h" extern int read_path_list_file(const tchar *listfile, tchar ***paths_ret, size_t *num_paths_ret, void **mem_ret); #endif /* _WIMLIB_PATHLIST_H */ wimlib-1.13.1/include/wimlib/compress_common.h0000644000175000017500000000065413030246726016273 00000000000000/* * compress_common.h * * Header for compression code shared by multiple compression formats. */ #ifndef _WIMLIB_COMPRESS_COMMON_H #define _WIMLIB_COMPRESS_COMMON_H #include "wimlib/types.h" extern void make_canonical_huffman_code(unsigned num_syms, unsigned max_codeword_len, const u32 freq_tab[restrict], u8 lens[restrict], u32 codewords[restrict]); #endif /* _WIMLIB_COMPRESS_COMMON_H */ wimlib-1.13.1/include/wimlib/lzms_common.h0000644000175000017500000001044413160354224015420 00000000000000/* * lzms_common.h * * Declarations shared between LZMS compression and decompression. */ #ifndef _LZMS_COMMON_H #define _LZMS_COMMON_H #include "wimlib/compiler.h" #include "wimlib/lzms_constants.h" #include "wimlib/types.h" /* Offset slot tables */ extern const u32 lzms_offset_slot_base[LZMS_MAX_NUM_OFFSET_SYMS + 1]; extern const u8 lzms_extra_offset_bits[LZMS_MAX_NUM_OFFSET_SYMS]; /* Length slot tables */ extern const u32 lzms_length_slot_base[LZMS_NUM_LENGTH_SYMS + 1]; extern const u8 lzms_extra_length_bits[LZMS_NUM_LENGTH_SYMS]; extern unsigned lzms_get_slot(u32 value, const u32 slot_base_tab[], unsigned num_slots); /* Return the offset slot for the specified offset */ static forceinline unsigned lzms_get_offset_slot(u32 offset) { return lzms_get_slot(offset, lzms_offset_slot_base, LZMS_MAX_NUM_OFFSET_SYMS); } /* Return the length slot for the specified length */ static forceinline unsigned lzms_get_length_slot(u32 length) { return lzms_get_slot(length, lzms_length_slot_base, LZMS_NUM_LENGTH_SYMS); } extern unsigned lzms_get_num_offset_slots(size_t uncompressed_size); /* Probability entry for use by the range coder when in a specific state */ struct lzms_probability_entry { /* The number of zeroes in the most recent LZMS_PROBABILITY_DENOMINATOR * bits that have been decoded or encoded using this probability entry. * The probability of the next bit being 0 is this value over * LZMS_PROBABILITY_DENOMINATOR, except for the cases where this would * imply 0% or 100% probability. */ u32 num_recent_zero_bits; /* The most recent LZMS_PROBABILITY_DENOMINATOR bits that have been * coded using this probability entry. The bits are ordered such that * low order is newest and high order is oldest. */ u64 recent_bits; }; struct lzms_probabilites { struct lzms_probability_entry main[LZMS_NUM_MAIN_PROBS]; struct lzms_probability_entry match[LZMS_NUM_MATCH_PROBS]; struct lzms_probability_entry lz[LZMS_NUM_LZ_PROBS]; struct lzms_probability_entry delta[LZMS_NUM_DELTA_PROBS]; struct lzms_probability_entry lz_rep[LZMS_NUM_LZ_REP_DECISIONS] [LZMS_NUM_LZ_REP_PROBS]; struct lzms_probability_entry delta_rep[LZMS_NUM_DELTA_REP_DECISIONS] [LZMS_NUM_DELTA_REP_PROBS]; }; extern void lzms_init_probabilities(struct lzms_probabilites *probs); /* Given a decoded or encoded bit, update the probability entry. */ static forceinline void lzms_update_probability_entry(struct lzms_probability_entry *entry, int bit) { STATIC_ASSERT(LZMS_PROBABILITY_DENOMINATOR == sizeof(entry->recent_bits) * 8); #ifdef __x86_64__ if (__builtin_constant_p(bit)) { /* Optimized implementation for x86_64 using carry flag */ if (bit) { __asm__("shlq %[recent_bits] \n" "adcl $0xffffffff, %[num_recent_zero_bits] \n" "orq $0x1, %[recent_bits] \n" : [recent_bits] "+r" (entry->recent_bits), [num_recent_zero_bits] "+mr" (entry->num_recent_zero_bits) : : "cc"); } else { __asm__("shlq %[recent_bits] \n" "adcl $0x0, %[num_recent_zero_bits] \n" : [recent_bits] "+m" (entry->recent_bits), [num_recent_zero_bits] "+mr" (entry->num_recent_zero_bits) : : "cc"); } } else #endif { s32 delta_zero_bits = (s32)(entry->recent_bits >> (LZMS_PROBABILITY_DENOMINATOR - 1)) - bit; entry->num_recent_zero_bits += delta_zero_bits; entry->recent_bits = (entry->recent_bits << 1) | bit; } } /* Given a probability entry, return the chance out of * LZMS_PROBABILITY_DENOMINATOR that the next decoded bit will be a 0. */ static forceinline u32 lzms_get_probability(const struct lzms_probability_entry *prob_entry) { u32 prob = prob_entry->num_recent_zero_bits; /* 0% and 100% probabilities aren't allowed. */ /* * if (prob == 0) * prob++; */ prob += (u32)(prob - 1) >> 31; /* * if (prob == LZMS_PROBABILITY_DENOMINATOR) * prob--; */ prob -= (prob >> LZMS_PROBABILITY_BITS); return prob; } extern void lzms_init_symbol_frequencies(u32 freqs[], unsigned num_syms); extern void lzms_dilute_symbol_frequencies(u32 freqs[], unsigned num_syms); /* Pre/post-processing */ extern void lzms_x86_filter(u8 data[], s32 size, s32 last_target_usages[], bool undo); #endif /* _LZMS_COMMON_H */ wimlib-1.13.1/include/wimlib/integrity.h0000644000175000017500000000117713160354224015104 00000000000000#ifndef _WIMLIB_INTEGRITY_H #define _WIMLIB_INTEGRITY_H #include #include "wimlib/types.h" #define WIM_INTEGRITY_OK 0 #define WIM_INTEGRITY_NOT_OK -1 #define WIM_INTEGRITY_NONEXISTENT -2 struct integrity_table; extern int read_integrity_table(WIMStruct *wim, u64 num_checked_bytes, struct integrity_table **table_ret); #define free_integrity_table(table) FREE(table) extern int write_integrity_table(WIMStruct *wim, off_t new_blob_table_end, off_t old_blob_table_end, struct integrity_table *old_table); extern int check_wim_integrity(WIMStruct *wim); #endif /* _WIMLIB_INTEGRITY_H */ wimlib-1.13.1/include/wimlib/reparse.h0000644000175000017500000000602113160354224014520 00000000000000#ifndef _WIMLIB_REPARSE_H #define _WIMLIB_REPARSE_H #include "wimlib/inode.h" /* for reparse tag definitions */ #include "wimlib/types.h" struct blob_descriptor; struct blob_table; /* Windows enforces this limit on the size of a reparse point buffer. */ #define REPARSE_POINT_MAX_SIZE 16384 /* * On-disk format of a reparse point buffer. See: * https://msdn.microsoft.com/en-us/library/dd541671.aspx * * Note: we are not using _packed_attribute for this structure, so only cast to * this if properly aligned! */ struct reparse_buffer_disk { le32 rptag; le16 rpdatalen; le16 rpreserved; union { u8 rpdata[REPARSE_POINT_MAX_SIZE - 8]; struct { le16 substitute_name_offset; le16 substitute_name_nbytes; le16 print_name_offset; le16 print_name_nbytes; union { struct { u8 data[REPARSE_POINT_MAX_SIZE - 16]; } junction; struct { le32 flags; #define SYMBOLIC_LINK_RELATIVE 0x00000001 u8 data[REPARSE_POINT_MAX_SIZE - 20]; } symlink; }; } link; }; }; #define REPARSE_DATA_OFFSET ((unsigned)offsetof(struct reparse_buffer_disk, rpdata)) #define REPARSE_DATA_MAX_SIZE (REPARSE_POINT_MAX_SIZE - REPARSE_DATA_OFFSET) static _unused_attribute void check_reparse_buffer_disk(void) { STATIC_ASSERT(offsetof(struct reparse_buffer_disk, rpdata) == 8); STATIC_ASSERT(offsetof(struct reparse_buffer_disk, link.junction.data) == 16); STATIC_ASSERT(offsetof(struct reparse_buffer_disk, link.symlink.data) == 20); STATIC_ASSERT(sizeof(struct reparse_buffer_disk) == REPARSE_POINT_MAX_SIZE); } /* Wrapper around a symbolic link or junction reparse point * (WIM_IO_REPARSE_TAG_SYMLINK or WIM_IO_REPARSE_TAG_MOUNT_POINT) */ struct link_reparse_point { u32 rptag; u16 rpreserved; /* Flags, valid for symbolic links only */ u32 symlink_flags; /* Pointers to the substitute name and print name of the link, * potentially not null terminated */ utf16lechar *substitute_name; utf16lechar *print_name; /* Lengths of the substitute and print names in bytes, not including * their null terminators if present */ size_t substitute_name_nbytes; size_t print_name_nbytes; }; static inline bool link_is_relative_symlink(const struct link_reparse_point *link) { return link->rptag == WIM_IO_REPARSE_TAG_SYMLINK && (link->symlink_flags & SYMBOLIC_LINK_RELATIVE); } extern void complete_reparse_point(struct reparse_buffer_disk *rpbuf, const struct wim_inode *inode, u16 blob_size); extern int parse_link_reparse_point(const struct reparse_buffer_disk *rpbuf, u16 rpbuflen, struct link_reparse_point *link); extern int make_link_reparse_point(const struct link_reparse_point *link, struct reparse_buffer_disk *rpbuf, u16 *rpbuflen_ret); #ifndef __WIN32__ extern int wim_inode_readlink(const struct wim_inode *inode, char *buf, size_t bufsize, const struct blob_descriptor *blob, const char *altroot, size_t altroot_len); extern int wim_inode_set_symlink(struct wim_inode *inode, const char *target, struct blob_table *blob_table); #endif #endif /* _WIMLIB_REPARSE_H */ wimlib-1.13.1/include/wimlib/error.h0000644000175000017500000000323613160354224014215 00000000000000#ifndef _WIMLIB_ERROR_H #define _WIMLIB_ERROR_H #include #include "wimlib.h" /* Get error code definitions */ #include "wimlib/compiler.h" #include "wimlib/types.h" static inline int _format_attribute(printf, 1, 2) dummy_tprintf(const tchar *format, ...) { return 0; } #ifdef ENABLE_ERROR_MESSAGES extern void wimlib_error(const tchar *format, ...) _format_attribute(printf, 1, 2) _cold_attribute; extern void wimlib_error_with_errno(const tchar *format, ...) _format_attribute(printf, 1, 2) _cold_attribute; extern void wimlib_warning(const tchar *format, ...) _format_attribute(printf, 1, 2) _cold_attribute; extern void wimlib_warning_with_errno(const tchar *format, ...) _format_attribute(printf, 1, 2) _cold_attribute; # define ERROR(format, ...) wimlib_error(T(format), ## __VA_ARGS__) # define ERROR_WITH_ERRNO(format, ...) wimlib_error_with_errno(T(format), ## __VA_ARGS__) # define WARNING(format, ...) wimlib_warning(T(format), ## __VA_ARGS__) # define WARNING_WITH_ERRNO(format, ...) wimlib_warning_with_errno(T(format), ## __VA_ARGS__) extern bool wimlib_print_errors; extern FILE *wimlib_error_file; #else /* ENABLE_ERROR_MESSAGES */ # define wimlib_print_errors 0 # define wimlib_error_file NULL # define ERROR(format, ...) dummy_tprintf(T(format), ## __VA_ARGS__) # define ERROR_WITH_ERRNO(format, ...) dummy_tprintf(T(format), ## __VA_ARGS__) # define WARNING(format, ...) dummy_tprintf(T(format), ## __VA_ARGS__) # define WARNING_WITH_ERRNO(format, ...) dummy_tprintf(T(format), ## __VA_ARGS__) #endif /* !ENABLE_ERROR_MESSAGES */ extern void print_byte_field(const u8 *field, size_t len, FILE *out); #endif /* _WIMLIB_ERROR_H */ wimlib-1.13.1/include/wimlib/object_id.h0000644000175000017500000000142213160354224015001 00000000000000#ifndef _WIMLIB_OBJECT_ID_H #define _WIMLIB_OBJECT_ID_H #include "wimlib/tagged_items.h" /* Unconfirmed: are all 64 bytes of the object ID always present? Since NTFS-3G * permits shorter object IDs, we'll do the same for now. */ #define OBJECT_ID_MIN_LENGTH 16 static inline const void * inode_get_object_id(const struct wim_inode *inode, u32 *len_ret) { return inode_get_tagged_item(inode, TAG_OBJECT_ID, OBJECT_ID_MIN_LENGTH, len_ret); } static inline bool inode_has_object_id(const struct wim_inode *inode) { return inode_get_object_id(inode, NULL) != NULL; } static inline bool inode_set_object_id(struct wim_inode *inode, const void *object_id, u32 len) { return inode_set_tagged_item(inode, TAG_OBJECT_ID, object_id, len); } #endif /* _WIMLIB_OBJECT_ID_H */ wimlib-1.13.1/include/wimlib/assert.h0000644000175000017500000000032513030246726014364 00000000000000#ifndef _WIMLIB_ASSERT_H #define _WIMLIB_ASSERT_H #ifdef ENABLE_ASSERTIONS #include # define wimlib_assert(expr) assert(expr) #else # define wimlib_assert(expr) #endif #endif /* _WIMLIB_ASSERT_H */ wimlib-1.13.1/include/wimlib/blob_table.h0000644000175000017500000002763513160354224015162 00000000000000#ifndef _WIMLIB_BLOB_TABLE_H #define _WIMLIB_BLOB_TABLE_H #include "wimlib/list.h" #include "wimlib/resource.h" #include "wimlib/sha1.h" #include "wimlib/types.h" /* An enumerated type that identifies where a blob's data is located. */ enum blob_location { /* The blob's data does not exist. This is a temporary state only. */ BLOB_NONEXISTENT = 0, /* The blob's data is available in the WIM resource identified by the * `struct wim_resource_descriptor' pointed to by @rdesc. * @offset_in_res identifies the offset at which this particular blob * begins in the uncompressed data of the resource. */ BLOB_IN_WIM, /* The blob's data is available as the contents of the file named by * @file_on_disk. */ BLOB_IN_FILE_ON_DISK, /* The blob's data is available as the contents of the in-memory buffer * pointed to by @attached_buffer. */ BLOB_IN_ATTACHED_BUFFER, #ifdef WITH_FUSE /* The blob's data is available as the contents of the file with name * @staging_file_name relative to the open directory file descriptor * @staging_dir_fd. */ BLOB_IN_STAGING_FILE, #endif #ifdef WITH_NTFS_3G /* The blob's data is available as the contents of an NTFS attribute * accessible through libntfs-3g. @ntfs_loc points to a structure which * identifies the attribute. */ BLOB_IN_NTFS_VOLUME, #endif #ifdef __WIN32__ /* Windows only: the blob's data is available in the file (or named data * stream) specified by @windows_file. The data might be only properly * accessible through the Windows API. */ BLOB_IN_WINDOWS_FILE, #endif }; /* A "blob extraction target" is a stream, and the inode to which that stream * belongs, to which a blob needs to be extracted as part of an extraction * operation. Since blobs are single-instanced, a blob may have multiple * extraction targets. */ struct blob_extraction_target { struct wim_inode *inode; struct wim_inode_stream *stream; }; /* * Descriptor for a "blob", which is a known length sequence of binary data. * * Within a WIM file, blobs are single instanced and are identified by SHA-1 * message digest. */ struct blob_descriptor { /* List node for a hash bucket of the blob table */ struct hlist_node hash_list; /* * Uncompressed size of this blob. * * In most cases we are now enforcing that this is nonzero; i.e. an * empty stream will have "no blob" rather than "an empty blob". The * exceptions are: * * - blob descriptors with 'blob_location == BLOB_NONEXISTENT', * e.g. placeholder entries for new metadata resources or for * blobs required for pipable WIM extraction. In these cases the * size is not meaningful information anyway. * - blob descriptors with 'blob_location == BLOB_IN_STAGING_FILE' * can vary their size over time, including to 0. */ u64 size; union { /* * For unhashed == 0: 'hash' is the SHA-1 message digest of the * blob's data. 'hash_short' allows accessing just a prefix of * the SHA-1 message digest, which is useful for getting a "hash * code" for hash table lookup/insertion. */ u8 hash[SHA1_HASH_SIZE]; size_t hash_short; /* For unhashed == 1: these variables make it possible to find * the stream that references this blob. There can be at most * one such reference, since duplicate blobs can only be joined * after they have been hashed. */ struct { struct wim_inode *back_inode; u32 back_stream_id; }; } _packed_attribute; /* union is SHA1_HASH_SIZE bytes */ /* Number of times this blob is referenced by file streams in WIM * images. See blob_decrement_refcnt() for information about the * limitations of this field. */ u32 refcnt; /* * When a WIM file is written, this is set to the number of references * (from file streams) to this blob in the output WIM file. * * During extraction, this is set to the number of targets to which this * blob is being extracted. * * During image export, this is set to the number of references of this * blob that originated from the source WIM. * * When mounting a WIM image read-write, this is set to the number of * extra references to this blob preemptively taken to allow later * saving the modified image as a new image and leaving the original * image alone. */ u32 out_refcnt; #ifdef WITH_FUSE /* Number of open file descriptors to this blob during a FUSE mount of * a WIM image. */ u16 num_opened_fds; #endif /* One of the `enum blob_location' values documented above. */ u16 blob_location : 4; /* 1 iff this blob contains "metadata" as opposed to data. */ u16 is_metadata : 1; /* 1 iff the SHA-1 message digest of this blob is unknown. */ u16 unhashed : 1; /* Temporary fields used when writing blobs; set as documented for * prepare_blob_list_for_write(). */ u16 unique_size : 1; u16 will_be_in_output_wim : 1; u16 may_send_done_with_file : 1; /* Only used by wimlib_export_image() */ u16 was_exported : 1; /* Specification of where this blob's data is located. Which member of * this union is valid is determined by the @blob_location field. */ union { /* BLOB_IN_WIM */ struct { struct wim_resource_descriptor *rdesc; u64 offset_in_res; /* Links together blobs that share the same underlying * WIM resource. The head is rdesc->blob_list. */ struct list_head rdesc_node; }; struct { union { /* BLOB_IN_FILE_ON_DISK * BLOB_IN_WINDOWS_FILE */ struct { union { tchar *file_on_disk; struct windows_file *windows_file; }; struct wim_inode *file_inode; }; /* BLOB_IN_ATTACHED_BUFFER */ void *attached_buffer; #ifdef WITH_FUSE /* BLOB_IN_STAGING_FILE */ struct { char *staging_file_name; int staging_dir_fd; }; #endif #ifdef WITH_NTFS_3G /* BLOB_IN_NTFS_VOLUME */ struct ntfs_location *ntfs_loc; #endif }; /* List link for per-WIM-image list of unhashed blobs */ struct list_head unhashed_list; }; }; /* Temporary fields */ union { /* Fields used temporarily during WIM file writing. */ struct { union { /* List node used for blob size table. */ struct hlist_node hash_list_2; /* Metadata for the underlying solid resource in * the WIM being written (only valid if * WIM_RESHDR_FLAG_SOLID set in * out_reshdr.flags). */ struct { u64 out_res_offset_in_wim; u64 out_res_size_in_wim; u64 out_res_uncompressed_size; }; }; /* Links blobs being written to the WIM. */ struct list_head write_blobs_list; union { /* Metadata for this blob in the WIM being * written. */ struct wim_reshdr out_reshdr; struct { /* Name under which this blob is being * sorted; used only when sorting blobs * for solid compression. */ utf16lechar *solid_sort_name; size_t solid_sort_name_nbytes; }; }; }; /* Used temporarily during extraction. This is an array of * references to the streams being extracted that use this blob. * out_refcnt tracks the number of slots filled. */ union { struct blob_extraction_target inline_blob_extraction_targets[3]; struct { struct blob_extraction_target *blob_extraction_targets; u32 alloc_blob_extraction_targets; }; }; }; /* Temporary list fields. */ union { /* Links blobs for writing blob table. */ struct list_head blob_table_list; /* Links blobs being extracted. */ struct list_head extraction_list; /* Links blobs being exported. */ struct list_head export_blob_list; }; }; extern struct blob_table * new_blob_table(size_t capacity) _malloc_attribute; extern void free_blob_table(struct blob_table *table); extern int read_blob_table(WIMStruct *wim); extern int write_blob_table_from_blob_list(struct list_head *blob_list, struct filedes *out_fd, u16 part_number, struct wim_reshdr *out_reshdr, int write_resource_flags); extern struct blob_descriptor * new_blob_descriptor(void) _malloc_attribute; extern struct blob_descriptor * clone_blob_descriptor(const struct blob_descriptor *blob) _malloc_attribute; extern void blob_decrement_refcnt(struct blob_descriptor *blob, struct blob_table *table); extern void blob_subtract_refcnt(struct blob_descriptor *blob, struct blob_table *table, u32 count); #ifdef WITH_FUSE extern void blob_decrement_num_opened_fds(struct blob_descriptor *blob); #endif extern void blob_release_location(struct blob_descriptor *blob); extern void free_blob_descriptor(struct blob_descriptor *blob); extern void blob_table_insert(struct blob_table *table, struct blob_descriptor *blob); extern void blob_table_unlink(struct blob_table *table, struct blob_descriptor *blob); extern struct blob_descriptor * lookup_blob(const struct blob_table *table, const u8 *hash); extern int for_blob_in_table(struct blob_table *table, int (*visitor)(struct blob_descriptor *, void *), void *arg); extern int for_blob_in_table_sorted_by_sequential_order(struct blob_table *table, int (*visitor)(struct blob_descriptor *, void *), void *arg); struct wimlib_resource_entry; extern void blob_to_wimlib_resource_entry(const struct blob_descriptor *blob, struct wimlib_resource_entry *wentry); extern int sort_blob_list(struct list_head *blob_list, size_t list_head_offset, int (*compar)(const void *, const void*)); extern int sort_blob_list_by_sequential_order(struct list_head *blob_list, size_t list_head_offset); extern int cmp_blobs_by_sequential_order(const void *p1, const void *p2); static inline const struct blob_extraction_target * blob_extraction_targets(const struct blob_descriptor *blob) { if (blob->out_refcnt <= ARRAY_LEN(blob->inline_blob_extraction_targets)) return blob->inline_blob_extraction_targets; else return blob->blob_extraction_targets; } /* * Declare that the specified blob is located in the specified WIM resource at * the specified offset. The caller is expected to set blob->size if required. */ static inline void blob_set_is_located_in_wim_resource(struct blob_descriptor *blob, struct wim_resource_descriptor *rdesc, u64 offset_in_res) { blob->blob_location = BLOB_IN_WIM; blob->rdesc = rdesc; list_add_tail(&blob->rdesc_node, &rdesc->blob_list); blob->offset_in_res = offset_in_res; } static inline void blob_unset_is_located_in_wim_resource(struct blob_descriptor *blob) { list_del(&blob->rdesc_node); blob->blob_location = BLOB_NONEXISTENT; } static inline void blob_set_is_located_in_attached_buffer(struct blob_descriptor *blob, void *buffer, size_t size) { blob->blob_location = BLOB_IN_ATTACHED_BUFFER; blob->attached_buffer = buffer; blob->size = size; } static inline bool blob_is_in_file(const struct blob_descriptor *blob) { return blob->blob_location == BLOB_IN_FILE_ON_DISK #ifdef __WIN32__ || blob->blob_location == BLOB_IN_WINDOWS_FILE #endif ; } #ifdef __WIN32__ extern const wchar_t * get_windows_file_path(const struct windows_file *file); #endif static inline const tchar * blob_file_path(const struct blob_descriptor *blob) { #ifdef __WIN32__ if (blob->blob_location == BLOB_IN_WINDOWS_FILE) return get_windows_file_path(blob->windows_file); #endif return blob->file_on_disk; } extern struct blob_descriptor * new_blob_from_data_buffer(const void *buffer, size_t size, struct blob_table *blob_table); extern struct blob_descriptor * after_blob_hashed(struct blob_descriptor *blob, struct blob_descriptor **back_ptr, struct blob_table *blob_table); extern int hash_unhashed_blob(struct blob_descriptor *blob, struct blob_table *blob_table, struct blob_descriptor **blob_ret); extern struct blob_descriptor ** retrieve_pointer_to_unhashed_blob(struct blob_descriptor *blob); static inline void prepare_unhashed_blob(struct blob_descriptor *blob, struct wim_inode *back_inode, u32 stream_id, struct list_head *unhashed_blobs) { if (!blob) return; blob->unhashed = 1; blob->back_inode = back_inode; blob->back_stream_id = stream_id; list_add_tail(&blob->unhashed_list, unhashed_blobs); } #endif /* _WIMLIB_BLOB_TABLE_H */ wimlib-1.13.1/include/wimlib/hc_matchfinder.h0000644000175000017500000003301713160354224016022 00000000000000/* * hc_matchfinder.h - Lempel-Ziv matchfinding with a hash table of linked lists * * The following copying information applies to this specific source code file: * * Written in 2014-2015 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . * * --------------------------------------------------------------------------- * * Algorithm * * This is a Hash Chains (hc) based matchfinder. * * The main data structure is a hash table where each hash bucket contains a * linked list (or "chain") of sequences whose first 4 bytes share the same hash * code. Each sequence is identified by its starting position in the input * buffer. * * The algorithm processes the input buffer sequentially. At each byte * position, the hash code of the first 4 bytes of the sequence beginning at * that position (the sequence being matched against) is computed. This * identifies the hash bucket to use for that position. Then, this hash * bucket's linked list is searched for matches. Then, a new linked list node * is created to represent the current sequence and is prepended to the list. * * This algorithm has several useful properties: * * - It only finds true Lempel-Ziv matches; i.e., those where the matching * sequence occurs prior to the sequence being matched against. * * - The sequences in each linked list are always sorted by decreasing starting * position. Therefore, the closest (smallest offset) matches are found * first, which in many compression formats tend to be the cheapest to encode. * * - Although fast running time is not guaranteed due to the possibility of the * lists getting very long, the worst degenerate behavior can be easily * prevented by capping the number of nodes searched at each position. * * - If the compressor decides not to search for matches at a certain position, * then that position can be quickly inserted without searching the list. * * - The algorithm is adaptable to sliding windows: just store the positions * relative to a "base" value that is updated from time to time, and stop * searching each list when the sequences get too far away. * * --------------------------------------------------------------------------- * * Notes on usage * * Before including this header, you must define 'mf_pos_t' to an integer type * that can represent all possible positions. This can be a 16-bit or 32-bit * unsigned integer. When possible, the former should be used due to the * reduced cache pressure. This header can be included multiple times in a * single .c file with different 'mf_pos_t' definitions; however, you must * define a different MF_SUFFIX each time to generate different names for the * matchfinder structure and functions. * * The number of bytes that must be allocated for a given 'struct * hc_matchfinder' must be gotten by calling hc_matchfinder_size(). * * ---------------------------------------------------------------------------- * * Optimizations * * The main hash table and chains handle length 4+ matches. Length 3 matches * are handled by a separate hash table with no chains. This works well for * typical "greedy" or "lazy"-style compressors, where length 3 matches are * often only helpful if they have small offsets. Instead of searching a full * chain for length 3+ matches, the algorithm just checks for one close length 3 * match, then focuses on finding length 4+ matches. * * The longest_match() and skip_positions() functions are inlined into the * compressors that use them. This isn't just about saving the overhead of a * function call. These functions are intended to be called from the inner * loops of compressors, where giving the compiler more control over register * allocation is very helpful. There is also significant benefit to be gained * from allowing the CPU to predict branches independently at each call site. * For example, "lazy"-style compressors can be written with two calls to * longest_match(), each of which starts with a different 'best_len' and * therefore has significantly different performance characteristics. * * Although any hash function can be used, a multiplicative hash is fast and * works well. * * On some processors, it is significantly faster to extend matches by whole * words (32 or 64 bits) instead of by individual bytes. For this to be the * case, the processor must implement unaligned memory accesses efficiently and * must have either a fast "find first set bit" instruction or a fast "find last * set bit" instruction, depending on the processor's endianness. * * The code uses one loop for finding the first match and one loop for finding a * longer match. Each of these loops is tuned for its respective task and in * combination are faster than a single generalized loop that handles both * tasks. * * The code also uses a tight inner loop that only compares the last and first * bytes of a potential match. It is only when these bytes match that a full * match extension is attempted. * * ---------------------------------------------------------------------------- */ #include #include "wimlib/lz_extend.h" #include "wimlib/lz_hash.h" #include "wimlib/unaligned.h" #define HC_MATCHFINDER_HASH3_ORDER 14 #define HC_MATCHFINDER_HASH4_ORDER 15 /* TEMPLATED functions and structures have MF_SUFFIX appended to their name. */ #undef TEMPLATED #define TEMPLATED(name) CONCAT(name, MF_SUFFIX) struct TEMPLATED(hc_matchfinder) { /* The hash table for finding length 3 matches */ mf_pos_t hash3_tab[1UL << HC_MATCHFINDER_HASH3_ORDER]; /* The hash table which contains the first nodes of the linked lists for * finding length 4+ matches */ mf_pos_t hash4_tab[1UL << HC_MATCHFINDER_HASH4_ORDER]; /* The "next node" references for the linked lists. The "next node" of * the node for the sequence with position 'pos' is 'next_tab[pos]'. */ mf_pos_t next_tab[]; }; /* Return the number of bytes that must be allocated for a 'hc_matchfinder' that * can work with buffers up to the specified size. */ static forceinline size_t TEMPLATED(hc_matchfinder_size)(size_t max_bufsize) { return sizeof(struct TEMPLATED(hc_matchfinder)) + (max_bufsize * sizeof(mf_pos_t)); } /* Prepare the matchfinder for a new input buffer. */ static forceinline void TEMPLATED(hc_matchfinder_init)(struct TEMPLATED(hc_matchfinder) *mf) { memset(mf, 0, sizeof(*mf)); } /* * Find the longest match longer than 'best_len' bytes. * * @mf * The matchfinder structure. * @in_begin * Pointer to the beginning of the input buffer. * @cur_pos * The current position in the input buffer (the position of the sequence * being matched against). * @best_len * Require a match longer than this length. * @max_len * The maximum permissible match length at this position. * @nice_len * Stop searching if a match of at least this length is found. * Must be <= @max_len. * @max_search_depth * Limit on the number of potential matches to consider. Must be >= 1. * @next_hashes * The precomputed hash codes for the sequence beginning at @in_next. * These will be used and then updated with the precomputed hashcodes for * the sequence beginning at @in_next + 1. * @offset_ret * If a match is found, its offset is returned in this location. * * Return the length of the match found, or 'best_len' if no match longer than * 'best_len' was found. */ static forceinline u32 TEMPLATED(hc_matchfinder_longest_match)(struct TEMPLATED(hc_matchfinder) * const restrict mf, const u8 * const restrict in_begin, const ptrdiff_t cur_pos, u32 best_len, const u32 max_len, const u32 nice_len, const u32 max_search_depth, u32 next_hashes[const restrict static 2], u32 * const restrict offset_ret) { const u8 *in_next = in_begin + cur_pos; u32 depth_remaining = max_search_depth; const u8 *best_matchptr = in_next; mf_pos_t cur_node3, cur_node4; u32 hash3, hash4; u32 next_seq3, next_seq4; u32 seq4; const u8 *matchptr; u32 len; if (unlikely(max_len < 5)) /* can we read 4 bytes from 'in_next + 1'? */ goto out; /* Get the precomputed hash codes. */ hash3 = next_hashes[0]; hash4 = next_hashes[1]; /* From the hash buckets, get the first node of each linked list. */ cur_node3 = mf->hash3_tab[hash3]; cur_node4 = mf->hash4_tab[hash4]; /* Update for length 3 matches. This replaces the singleton node in the * 'hash3' bucket with the node for the current sequence. */ mf->hash3_tab[hash3] = cur_pos; /* Update for length 4 matches. This prepends the node for the current * sequence to the linked list in the 'hash4' bucket. */ mf->hash4_tab[hash4] = cur_pos; mf->next_tab[cur_pos] = cur_node4; /* Compute the next hash codes. */ next_seq4 = load_u32_unaligned(in_next + 1); next_seq3 = loaded_u32_to_u24(next_seq4); next_hashes[0] = lz_hash(next_seq3, HC_MATCHFINDER_HASH3_ORDER); next_hashes[1] = lz_hash(next_seq4, HC_MATCHFINDER_HASH4_ORDER); prefetchw(&mf->hash3_tab[next_hashes[0]]); prefetchw(&mf->hash4_tab[next_hashes[1]]); if (best_len < 4) { /* No match of length >= 4 found yet? */ /* Check for a length 3 match if needed. */ if (!cur_node3) goto out; seq4 = load_u32_unaligned(in_next); if (best_len < 3) { matchptr = &in_begin[cur_node3]; if (load_u24_unaligned(matchptr) == loaded_u32_to_u24(seq4)) { best_len = 3; best_matchptr = matchptr; } } /* Check for a length 4 match. */ if (!cur_node4) goto out; for (;;) { /* No length 4 match found yet. Check the first 4 bytes. */ matchptr = &in_begin[cur_node4]; if (load_u32_unaligned(matchptr) == seq4) break; /* The first 4 bytes did not match. Keep trying. */ cur_node4 = mf->next_tab[cur_node4]; if (!cur_node4 || !--depth_remaining) goto out; } /* Found a match of length >= 4. Extend it to its full length. */ best_matchptr = matchptr; best_len = lz_extend(in_next, best_matchptr, 4, max_len); if (best_len >= nice_len) goto out; cur_node4 = mf->next_tab[cur_node4]; if (!cur_node4 || !--depth_remaining) goto out; } else { if (!cur_node4 || best_len >= nice_len) goto out; } /* Check for matches of length >= 5. */ for (;;) { for (;;) { matchptr = &in_begin[cur_node4]; /* Already found a length 4 match. Try for a longer * match; start by checking either the last 4 bytes and * the first 4 bytes, or the last byte. (The last byte, * the one which would extend the match length by 1, is * the most important.) */ #if UNALIGNED_ACCESS_IS_FAST if ((load_u32_unaligned(matchptr + best_len - 3) == load_u32_unaligned(in_next + best_len - 3)) && (load_u32_unaligned(matchptr) == load_u32_unaligned(in_next))) #else if (matchptr[best_len] == in_next[best_len]) #endif break; /* Continue to the next node in the list. */ cur_node4 = mf->next_tab[cur_node4]; if (!cur_node4 || !--depth_remaining) goto out; } #if UNALIGNED_ACCESS_IS_FAST len = 4; #else len = 0; #endif len = lz_extend(in_next, matchptr, len, max_len); if (len > best_len) { /* This is the new longest match. */ best_len = len; best_matchptr = matchptr; if (best_len >= nice_len) goto out; } /* Continue to the next node in the list. */ cur_node4 = mf->next_tab[cur_node4]; if (!cur_node4 || !--depth_remaining) goto out; } out: *offset_ret = in_next - best_matchptr; return best_len; } /* * Advance the matchfinder, but don't search for matches. * * @mf * The matchfinder structure. * @in_begin * Pointer to the beginning of the input buffer. * @cur_pos * The current position in the input buffer (the position of the sequence * being matched against). * @end_pos * The length of the input buffer. * @next_hashes * The precomputed hash codes for the sequence beginning at @in_next. * These will be used and then updated with the precomputed hashcodes for * the sequence beginning at @in_next + @count. * @count * The number of bytes to advance. Must be > 0. * * Returns @in_next + @count. */ static forceinline const u8 * TEMPLATED(hc_matchfinder_skip_positions)(struct TEMPLATED(hc_matchfinder) * const restrict mf, const u8 * const restrict in_begin, const ptrdiff_t cur_pos, const ptrdiff_t end_pos, const u32 count, u32 next_hashes[const restrict static 2]) { const u8 *in_next = in_begin + cur_pos; const u8 * const stop_ptr = in_next + count; if (likely(count + 5 <= end_pos - cur_pos)) { u32 hash3, hash4; u32 next_seq3, next_seq4; hash3 = next_hashes[0]; hash4 = next_hashes[1]; do { mf->hash3_tab[hash3] = in_next - in_begin; mf->next_tab[in_next - in_begin] = mf->hash4_tab[hash4]; mf->hash4_tab[hash4] = in_next - in_begin; next_seq4 = load_u32_unaligned(++in_next); next_seq3 = loaded_u32_to_u24(next_seq4); hash3 = lz_hash(next_seq3, HC_MATCHFINDER_HASH3_ORDER); hash4 = lz_hash(next_seq4, HC_MATCHFINDER_HASH4_ORDER); } while (in_next != stop_ptr); prefetchw(&mf->hash3_tab[hash3]); prefetchw(&mf->hash4_tab[hash4]); next_hashes[0] = hash3; next_hashes[1] = hash4; } return stop_ptr; } wimlib-1.13.1/include/wimlib/timestamp.h0000644000175000017500000000154213272231267015072 00000000000000/* * timestamp.h * * Conversion between Windows NT timestamps and UNIX timestamps. */ #ifndef _WIMLIB_TIMESTAMP_H #define _WIMLIB_TIMESTAMP_H #include #include #include "wimlib/types.h" struct wimlib_timespec; extern time_t wim_timestamp_to_time_t(u64 timestamp); extern void wim_timestamp_to_wimlib_timespec(u64 timestamp, struct wimlib_timespec *wts, s32 *high_part_ret); extern struct timeval wim_timestamp_to_timeval(u64 timestamp); extern struct timespec wim_timestamp_to_timespec(u64 timestamp); extern u64 time_t_to_wim_timestamp(time_t t); extern u64 timeval_to_wim_timestamp(const struct timeval *tv); extern u64 timespec_to_wim_timestamp(const struct timespec *ts); extern u64 now_as_wim_timestamp(void); extern void wim_timestamp_to_str(u64 timestamp, tchar *buf, size_t len); #endif /* _WIMLIB_TIMESTAMP_H */ wimlib-1.13.1/include/wimlib/divsufsort.h0000644000175000017500000000035013160354224015266 00000000000000#ifndef _WIMLIB_DIVSUFSORT_H #define _WIMLIB_DIVSUFSORT_H #include "wimlib/types.h" extern void divsufsort(const u8 *T, u32 *SA, u32 n, u32 *tmp); #define DIVSUFSORT_TMP_LEN (256 + (256 * 256)) #endif /* _WIMLIB_DIVSUFSORT_H */ wimlib-1.13.1/include/wimlib/lcpit_matchfinder.h0000644000175000017500000000356313454550566016563 00000000000000/* * lcpit_matchfinder.h * * A match-finder for Lempel-Ziv compression based on bottom-up construction and * traversal of the Longest Common Prefix (LCP) interval tree. * * The following copying information applies to this specific source code file: * * Written in 2014-2015 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . */ #ifndef _LCPIT_MATCHFINDER_H #define _LCPIT_MATCHFINDER_H #include "wimlib/types.h" struct lcpit_matchfinder { bool huge_mode; u32 cur_pos; u32 *pos_data; union { u32 *intervals; u64 *intervals64; }; u32 min_match_len; u32 nice_match_len; u32 next[2]; u32 orig_nice_match_len; }; struct lz_match { u32 length; u32 offset; }; extern u64 lcpit_matchfinder_get_needed_memory(size_t max_bufsize); extern bool lcpit_matchfinder_init(struct lcpit_matchfinder *mf, size_t max_bufsize, u32 min_match_len, u32 nice_match_len); extern void lcpit_matchfinder_load_buffer(struct lcpit_matchfinder *mf, const u8 *T, u32 n); extern u32 lcpit_matchfinder_get_matches(struct lcpit_matchfinder *mf, struct lz_match *matches); extern void lcpit_matchfinder_skip_bytes(struct lcpit_matchfinder *mf, u32 count); extern void lcpit_matchfinder_destroy(struct lcpit_matchfinder *mf); #endif /* _LCPIT_MATCHFINDER_H */ wimlib-1.13.1/include/wimlib/endianness.h0000644000175000017500000000764713324310651015223 00000000000000/* * endianness.h - macros and inline functions for endianness conversion * * The following copying information applies to this specific source code file: * * Written in 2014-2015 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . */ #ifndef _WIMLIB_ENDIANNESS_H #define _WIMLIB_ENDIANNESS_H #include "wimlib/compiler.h" #include "wimlib/types.h" #ifdef HAVE_SYS_ENDIAN_H /* Needed on NetBSD to stop system bswap macros from messing things up */ # include # undef bswap16 # undef bswap32 # undef bswap64 #endif /* Watch out for conflict with ntfs-3g/endians.h ... */ #ifndef _NTFS_ENDIANS_H #define bswap16_const(n) \ ((((u16)(n) & 0x00FF) << 8) | \ (((u16)(n) & 0xFF00) >> 8)) #define bswap32_const(n) \ ((((u32)(n) & 0x000000FF) << 24) | \ (((u32)(n) & 0x0000FF00) << 8) | \ (((u32)(n) & 0x00FF0000) >> 8) | \ (((u32)(n) & 0xFF000000) >> 24)) #define bswap64_const(n) \ ((((u64)(n) & 0x00000000000000FF) << 56) | \ (((u64)(n) & 0x000000000000FF00) << 40) | \ (((u64)(n) & 0x0000000000FF0000) << 24) | \ (((u64)(n) & 0x00000000FF000000) << 8) | \ (((u64)(n) & 0x000000FF00000000) >> 8) | \ (((u64)(n) & 0x0000FF0000000000) >> 24) | \ (((u64)(n) & 0x00FF000000000000) >> 40) | \ (((u64)(n) & 0xFF00000000000000) >> 56)) static forceinline u16 do_bswap16(u16 n) { #ifdef compiler_bswap16 return compiler_bswap16(n); #else return bswap16_const(n); #endif } static forceinline u32 do_bswap32(u32 n) { #ifdef compiler_bswap32 return compiler_bswap32(n); #else return bswap32_const(n); #endif } static forceinline u64 do_bswap64(u64 n) { #ifdef compiler_bswap64 return compiler_bswap64(n); #else return bswap64_const(n); #endif } #define bswap16(n) (__builtin_constant_p(n) ? bswap16_const(n) : do_bswap16(n)) #define bswap32(n) (__builtin_constant_p(n) ? bswap32_const(n) : do_bswap32(n)) #define bswap64(n) (__builtin_constant_p(n) ? bswap64_const(n) : do_bswap64(n)) #if CPU_IS_BIG_ENDIAN # define cpu_to_le16(n) ((_force_attr le16)bswap16(n)) # define cpu_to_le32(n) ((_force_attr le32)bswap32(n)) # define cpu_to_le64(n) ((_force_attr le64)bswap64(n)) # define le16_to_cpu(n) bswap16((_force_attr u16)(le16)(n)) # define le32_to_cpu(n) bswap32((_force_attr u32)(le32)(n)) # define le64_to_cpu(n) bswap64((_force_attr u64)(le64)(n)) # define cpu_to_be16(n) ((_force_attr be16)(u16)(n)) # define cpu_to_be32(n) ((_force_attr be32)(u32)(n)) # define cpu_to_be64(n) ((_force_attr be64)(u64)(n)) # define be16_to_cpu(n) ((_force_attr u16)(be16)(n)) # define be32_to_cpu(n) ((_force_attr u32)(be32)(n)) # define be64_to_cpu(n) ((_force_attr u64)(be64)(n)) #else # define cpu_to_le16(n) ((_force_attr le16)(u16)(n)) # define cpu_to_le32(n) ((_force_attr le32)(u32)(n)) # define cpu_to_le64(n) ((_force_attr le64)(u64)(n)) # define le16_to_cpu(n) ((_force_attr u16)(le16)(n)) # define le32_to_cpu(n) ((_force_attr u32)(le32)(n)) # define le64_to_cpu(n) ((_force_attr u64)(le64)(n)) # define cpu_to_be16(n) ((_force_attr be16)bswap16(n)) # define cpu_to_be32(n) ((_force_attr be32)bswap32(n)) # define cpu_to_be64(n) ((_force_attr be64)bswap64(n)) # define be16_to_cpu(n) bswap16((_force_attr u16)(be16)(n)) # define be32_to_cpu(n) bswap32((_force_attr u32)(be32)(n)) # define be64_to_cpu(n) bswap64((_force_attr u64)(be64)(n)) #endif #endif /* _NTFS_ENDIANS_H */ #endif /* _WIMLIB_ENDIANNESS_H */ wimlib-1.13.1/include/wimlib/compressor_ops.h0000644000175000017500000000156313277475672016166 00000000000000/* * compressor_ops.h * * Interface implemented by compressors for specific formats. */ #ifndef _WIMLIB_COMPRESSOR_OPS_H #define _WIMLIB_COMPRESSOR_OPS_H #include "wimlib/types.h" struct compressor_ops { u64 (*get_needed_memory)(size_t max_block_size, unsigned int compression_level, bool destructive); int (*create_compressor)(size_t max_block_size, unsigned int compression_level, bool destructive, void **private_ret); size_t (*compress)(const void *uncompressed_data, size_t uncompressed_size, void *compressed_data, size_t compressed_size_avail, void *private); void (*free_compressor)(void *private); }; extern const struct compressor_ops lzx_compressor_ops; extern const struct compressor_ops xpress_compressor_ops; extern const struct compressor_ops lzms_compressor_ops; #endif /* _WIMLIB_COMPRESSOR_OPS_H */ wimlib-1.13.1/include/wimlib/solid.h0000644000175000017500000000026013160354224014170 00000000000000#ifndef _WIMLIB_SOLID_H #define _WIMLIB_SOLID_H struct list_head; extern int sort_blob_list_for_solid_compression(struct list_head *blob_list); #endif /* _WIMLIB_SOLID_H */ wimlib-1.13.1/include/wimlib/bitops.h0000644000175000017500000000526613160354224014371 00000000000000/* * bitops.h - inline functions for bit manipulation * * The following copying information applies to this specific source code file: * * Written in 2014-2016 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . */ #ifndef _WIMLIB_BITOPS_H #define _WIMLIB_BITOPS_H #include "wimlib/compiler.h" #include "wimlib/types.h" /* * Bit Scan Reverse (BSR) - find the 0-based index (relative to the least * significant bit) of the *most* significant 1 bit in the input value. The * input value must be nonzero! */ static forceinline unsigned bsr32(u32 v) { #ifdef compiler_bsr32 return compiler_bsr32(v); #else unsigned bit = 0; while ((v >>= 1) != 0) bit++; return bit; #endif } static forceinline unsigned bsr64(u64 v) { #ifdef compiler_bsr64 return compiler_bsr64(v); #else unsigned bit = 0; while ((v >>= 1) != 0) bit++; return bit; #endif } static forceinline unsigned bsrw(machine_word_t v) { STATIC_ASSERT(WORDBITS == 32 || WORDBITS == 64); if (WORDBITS == 32) return bsr32(v); else return bsr64(v); } /* * Bit Scan Forward (BSF) - find the 0-based index (relative to the least * significant bit) of the *least* significant 1 bit in the input value. The * input value must be nonzero! */ static forceinline unsigned bsf32(u32 v) { #ifdef compiler_bsf32 return compiler_bsf32(v); #else unsigned bit; for (bit = 0; !(v & 1); bit++, v >>= 1) ; return bit; #endif } static forceinline unsigned bsf64(u64 v) { #ifdef compiler_bsf64 return compiler_bsf64(v); #else unsigned bit; for (bit = 0; !(v & 1); bit++, v >>= 1) ; return bit; #endif } static forceinline unsigned bsfw(machine_word_t v) { STATIC_ASSERT(WORDBITS == 32 || WORDBITS == 64); if (WORDBITS == 32) return bsf32(v); else return bsf64(v); } /* Return the log base 2 of 'n', rounded up to the nearest integer. */ static forceinline unsigned ilog2_ceil(size_t n) { if (n <= 1) return 0; return 1 + bsrw(n - 1); } /* Round 'n' up to the nearest power of 2 */ static forceinline size_t roundup_pow_of_2(size_t n) { return (size_t)1 << ilog2_ceil(n); } #endif /* _WIMLIB_BITOPS_H */ wimlib-1.13.1/include/wimlib/xpress_constants.h0000644000175000017500000000065313030246726016507 00000000000000/* * xpress_constants.h * * Constants for the XPRESS compression format. */ #ifndef _XPRESS_CONSTANTS_H #define _XPRESS_CONSTANTS_H #define XPRESS_NUM_CHARS 256 #define XPRESS_NUM_SYMBOLS 512 #define XPRESS_MAX_CODEWORD_LEN 15 #define XPRESS_END_OF_DATA 256 #define XPRESS_MIN_OFFSET 1 #define XPRESS_MAX_OFFSET 65535 #define XPRESS_MIN_MATCH_LEN 3 #define XPRESS_MAX_MATCH_LEN 65538 #endif /* _XPRESS_CONSTANTS_H */ wimlib-1.13.1/include/wimlib/apply.h0000644000175000017500000002111413272231267014211 00000000000000#ifndef _WIMLIB_APPLY_H #define _WIMLIB_APPLY_H #include "wimlib/compiler.h" #include "wimlib/file_io.h" #include "wimlib/list.h" #include "wimlib/progress.h" #include "wimlib/types.h" #include "wimlib.h" /* These can be treated as counts (for required_features) or booleans (for * supported_features). */ struct wim_features { unsigned long readonly_files; unsigned long hidden_files; unsigned long system_files; unsigned long archive_files; unsigned long compressed_files; unsigned long encrypted_files; unsigned long encrypted_directories; unsigned long not_context_indexed_files; unsigned long sparse_files; unsigned long named_data_streams; unsigned long hard_links; unsigned long reparse_points; unsigned long symlink_reparse_points; unsigned long other_reparse_points; unsigned long security_descriptors; unsigned long short_names; unsigned long unix_data; unsigned long object_ids; unsigned long timestamps; unsigned long case_sensitive_filenames; unsigned long xattrs; }; struct blob_descriptor; struct read_blob_callbacks; struct apply_operations; struct wim_dentry; struct apply_ctx { /* The WIMStruct from which files are being extracted from the currently * selected image. */ WIMStruct *wim; /* The target of the extraction, usually the path to a directory. */ const tchar *target; /* Length of @target in tchars. */ size_t target_nchars; /* Extraction flags (WIMLIB_EXTRACT_FLAG_*) */ int extract_flags; /* User-provided progress function, or NULL if not specified. */ wimlib_progress_func_t progfunc; void *progctx; /* Progress data buffer, with progress.extract initialized. */ union wimlib_progress_info progress; /* Features required to extract the files (with counts) */ struct wim_features required_features; /* Features supported by the extraction mode (with booleans) */ struct wim_features supported_features; /* The members below should not be used outside of extract.c */ const struct apply_operations *apply_ops; u64 next_progress; unsigned long invalid_sequence; unsigned long num_blobs_remaining; struct list_head blob_list; const struct read_blob_callbacks *saved_cbs; struct filedes tmpfile_fd; tchar *tmpfile_name; unsigned int count_until_file_progress; }; /* Maximum number of UNIX file descriptors, NTFS attributes, or Windows file * handles that can be opened simultaneously to extract a blob to multiple * destinations. */ #define MAX_OPEN_FILES 512 static inline int extract_progress(struct apply_ctx *ctx, enum wimlib_progress_msg msg) { return call_progress(ctx->progfunc, msg, &ctx->progress, ctx->progctx); } extern int do_file_extract_progress(struct apply_ctx *ctx, enum wimlib_progress_msg msg); #define COUNT_PER_FILE_PROGRESS 256 static inline int maybe_do_file_progress(struct apply_ctx *ctx, enum wimlib_progress_msg msg) { ctx->progress.extract.current_file_count++; if (unlikely(!--ctx->count_until_file_progress)) return do_file_extract_progress(ctx, msg); return 0; } extern int start_file_structure_phase(struct apply_ctx *ctx, u64 end_file_count); extern int start_file_metadata_phase(struct apply_ctx *ctx, u64 end_file_count); /* Report that a file was created, prior to blob extraction. */ static inline int report_file_created(struct apply_ctx *ctx) { return maybe_do_file_progress(ctx, WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE); } /* Report that file metadata was applied, after blob extraction. */ static inline int report_file_metadata_applied(struct apply_ctx *ctx) { return maybe_do_file_progress(ctx, WIMLIB_PROGRESS_MSG_EXTRACT_METADATA); } extern int end_file_structure_phase(struct apply_ctx *ctx); extern int end_file_metadata_phase(struct apply_ctx *ctx); static inline int report_apply_error(struct apply_ctx *ctx, int error_code, const tchar *path) { return report_error(ctx->progfunc, ctx->progctx, error_code, path); } extern bool detect_sparse_region(const void *data, size_t size, size_t *len_ret); static inline bool maybe_detect_sparse_region(const void *data, size_t size, size_t *len_ret, bool enabled) { if (!enabled) { /* Force non-sparse without checking */ *len_ret = size; return false; } return detect_sparse_region(data, size, len_ret); } #define inode_first_extraction_dentry(inode) \ ((inode)->i_first_extraction_alias) #define inode_for_each_extraction_alias(dentry, inode) \ for (dentry = inode_first_extraction_dentry(inode); \ dentry != NULL; \ dentry = dentry->d_next_extraction_alias) extern int extract_blob_list(struct apply_ctx *ctx, const struct read_blob_callbacks *cbs); /* * Represents an extraction backend. */ struct apply_operations { /* Name of the extraction backend. */ const char *name; /* * Query the features supported by the extraction backend. * * @target * The target string that was provided by the user. (Often a * directory, but extraction backends are free to interpret this * differently.) * * @supported_features * A structure, each of whose members represents a feature that may * be supported by the extraction backend. For each feature that * the extraction backend supports, this routine must set the * corresponding member to a nonzero value. * * Return 0 if successful; otherwise a positive wimlib error code. */ int (*get_supported_features)(const tchar *target, struct wim_features *supported_features); /* * Main extraction routine. * * The extraction backend is provided a list of dentries that have been * prepared for extraction. It is free to extract them in any way that * it chooses. Ideally, it should choose a method that maximizes * performance. * * The target string will be provided in ctx->common.target. This might * be a directory, although extraction backends are free to interpret it * as they wish. TODO: in some cases, the common extraction code also * interprets the target string. This should be completely isolated to * extraction backends. * * The extraction flags will be provided in ctx->common.extract_flags. * Extraction backends should examine them and implement the behaviors * for as many flags as possible. Some flags are already handled by the * common extraction code. TODO: this needs to be better formalized. * * @dentry_list, the list of dentries, will be ordered such that the * ancestor of any dentry always precedes any descendents. Unless * @single_tree_only is set, it's possible that the dentries consist of * multiple disconnected trees. * * 'd_extraction_name' and 'd_extraction_name_nchars' of each dentry * will be set to indicate the actual name with which the dentry should * be extracted. This may or may not be the same as 'd_name'. TODO: * really, the extraction backends should be responsible for generating * 'd_extraction_name'. * * Each dentry will refer to a valid inode in 'd_inode'. Each inode * will contain a list of dentries of that inode being extracted; this * list may be shorter than the inode's full dentry list. * * The blobs required to be extracted will already be prepared in * 'apply_ctx'. The extraction backend should call extract_blob_list() * to extract them. * * The will_extract_dentry() utility function, given an arbitrary dentry * in the WIM image (which may not be in the extraction list), can be * used to determine if that dentry is in the extraction list. * * Return 0 if successful; otherwise a positive wimlib error code. */ int (*extract)(struct list_head *dentry_list, struct apply_ctx *ctx); /* * Query whether the unnamed data stream of the specified file will be * extracted as "externally backed" from the WIM archive itself. If so, * then the extraction backend is assumed to handle this separately, and * the common extraction code will not register a usage of the unnamed * data stream's blob. * * This routine is optional. * * Return: * < 0 if the file will *not* be externally backed. * = 0 if the file will be externally backed. * > 0 (wimlib error code) if another error occurred. */ int (*will_back_from_wim)(struct wim_dentry *dentry, struct apply_ctx *ctx); /* * Size of the backend-specific extraction context. It must contain * 'struct apply_ctx' as its first member. */ size_t context_size; /* * Set this if the extraction backend only supports extracting dentries * that form a single tree, not multiple trees. */ bool single_tree_only; }; #ifdef __WIN32__ extern const struct apply_operations win32_apply_ops; #else extern const struct apply_operations unix_apply_ops; #endif #ifdef WITH_NTFS_3G extern const struct apply_operations ntfs_3g_apply_ops; #endif #endif /* _WIMLIB_APPLY_H */ wimlib-1.13.1/include/wimlib/pattern.h0000644000175000017500000000135613324665151014550 00000000000000#ifndef _WIMLIB_PATTERN_H #define _WIMLIB_PATTERN_H #include "wimlib/types.h" struct wim_dentry; /* Flags for match_path() and match_pattern_list() */ /* * If set, subdirectories (and sub-files) are also matched. * For example, the pattern "/dir" would match the path "/dir/file". */ #define MATCH_RECURSIVELY 0x01 /* * If set, ancestor directories are also matched. * For example, the pattern "/dir/file" would match the path "/dir". */ #define MATCH_ANCESTORS 0x02 extern bool match_path(const tchar *path, const tchar *pattern, int match_flags); extern int expand_path_pattern(struct wim_dentry *root, const tchar *pattern, int (*consume_dentry)(struct wim_dentry *, void *), void *ctx); #endif /* _WIMLIB_PATTERN_H */ wimlib-1.13.1/include/wimlib/decompress_common.h0000644000175000017500000004757513461156352016624 00000000000000/* * decompress_common.h * * Header for decompression code shared by multiple compression formats. * * The following copying information applies to this specific source code file: * * Written in 2012-2016 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . */ #ifndef _WIMLIB_DECOMPRESS_COMMON_H #define _WIMLIB_DECOMPRESS_COMMON_H #include #include "wimlib/compiler.h" #include "wimlib/types.h" #include "wimlib/unaligned.h" /******************************************************************************/ /* Input bitstream for XPRESS and LZX */ /*----------------------------------------------------------------------------*/ /* Structure that encapsulates a block of in-memory data being interpreted as a * stream of bits, optionally with interwoven literal bytes. Bits are assumed * to be stored in little endian 16-bit coding units, with the bits ordered high * to low. */ struct input_bitstream { /* Bits that have been read from the input buffer. The bits are * left-justified; the next bit is always bit 31. */ u32 bitbuf; /* Number of bits currently held in @bitbuf. */ u32 bitsleft; /* Pointer to the next byte to be retrieved from the input buffer. */ const u8 *next; /* Pointer past the end of the input buffer. */ const u8 *end; }; /* Initialize a bitstream to read from the specified input buffer. */ static forceinline void init_input_bitstream(struct input_bitstream *is, const void *buffer, u32 size) { is->bitbuf = 0; is->bitsleft = 0; is->next = buffer; is->end = is->next + size; } /* Note: for performance reasons, the following methods don't return error codes * to the caller if the input buffer is overrun. Instead, they just assume that * all overrun data is zeroes. This has no effect on well-formed compressed * data. The only disadvantage is that bad compressed data may go undetected, * but even this is irrelevant if higher level code checksums the uncompressed * data anyway. */ /* Ensure the bit buffer variable for the bitstream contains at least @num_bits * bits. Following this, bitstream_peek_bits() and/or bitstream_remove_bits() * may be called on the bitstream to peek or remove up to @num_bits bits. */ static forceinline void bitstream_ensure_bits(struct input_bitstream *is, const unsigned num_bits) { /* This currently works for at most 17 bits. */ if (is->bitsleft >= num_bits) return; if (unlikely(is->end - is->next < 2)) goto overflow; is->bitbuf |= (u32)get_unaligned_le16(is->next) << (16 - is->bitsleft); is->next += 2; is->bitsleft += 16; if (unlikely(num_bits == 17 && is->bitsleft == 16)) { if (unlikely(is->end - is->next < 2)) goto overflow; is->bitbuf |= (u32)get_unaligned_le16(is->next); is->next += 2; is->bitsleft = 32; } return; overflow: is->bitsleft = 32; } /* Return the next @num_bits bits from the bitstream, without removing them. * There must be at least @num_bits remaining in the buffer variable, from a * previous call to bitstream_ensure_bits(). */ static forceinline u32 bitstream_peek_bits(const struct input_bitstream *is, const unsigned num_bits) { return (is->bitbuf >> 1) >> (sizeof(is->bitbuf) * 8 - num_bits - 1); } /* Remove @num_bits from the bitstream. There must be at least @num_bits * remaining in the buffer variable, from a previous call to * bitstream_ensure_bits(). */ static forceinline void bitstream_remove_bits(struct input_bitstream *is, unsigned num_bits) { is->bitbuf <<= num_bits; is->bitsleft -= num_bits; } /* Remove and return @num_bits bits from the bitstream. There must be at least * @num_bits remaining in the buffer variable, from a previous call to * bitstream_ensure_bits(). */ static forceinline u32 bitstream_pop_bits(struct input_bitstream *is, unsigned num_bits) { u32 bits = bitstream_peek_bits(is, num_bits); bitstream_remove_bits(is, num_bits); return bits; } /* Read and return the next @num_bits bits from the bitstream. */ static forceinline u32 bitstream_read_bits(struct input_bitstream *is, unsigned num_bits) { bitstream_ensure_bits(is, num_bits); return bitstream_pop_bits(is, num_bits); } /* Read and return the next literal byte embedded in the bitstream. */ static forceinline u8 bitstream_read_byte(struct input_bitstream *is) { if (unlikely(is->end == is->next)) return 0; return *is->next++; } /* Read and return the next 16-bit integer embedded in the bitstream. */ static forceinline u16 bitstream_read_u16(struct input_bitstream *is) { u16 v; if (unlikely(is->end - is->next < 2)) return 0; v = get_unaligned_le16(is->next); is->next += 2; return v; } /* Read and return the next 32-bit integer embedded in the bitstream. */ static forceinline u32 bitstream_read_u32(struct input_bitstream *is) { u32 v; if (unlikely(is->end - is->next < 4)) return 0; v = get_unaligned_le32(is->next); is->next += 4; return v; } /* Read into @dst_buffer an array of literal bytes embedded in the bitstream. * Return 0 if there were enough bytes remaining in the input, otherwise -1. */ static forceinline int bitstream_read_bytes(struct input_bitstream *is, void *dst_buffer, size_t count) { if (unlikely(is->end - is->next < count)) return -1; memcpy(dst_buffer, is->next, count); is->next += count; return 0; } /* Align the input bitstream on a coding-unit boundary. */ static forceinline void bitstream_align(struct input_bitstream *is) { is->bitsleft = 0; is->bitbuf = 0; } /******************************************************************************/ /* Huffman decoding */ /*----------------------------------------------------------------------------*/ /* * Required alignment for the Huffman decode tables. We require this alignment * so that we can fill the entries with vector or word instructions and not have * to deal with misaligned buffers. */ #define DECODE_TABLE_ALIGNMENT 16 /* * Each decode table entry is 16 bits divided into two fields: 'symbol' (high 12 * bits) and 'length' (low 4 bits). The precise meaning of these fields depends * on the type of entry: * * Root table entries which are *not* subtable pointers: * symbol: symbol to decode * length: codeword length in bits * * Root table entries which are subtable pointers: * symbol: index of start of subtable * length: number of bits with which the subtable is indexed * * Subtable entries: * symbol: symbol to decode * length: codeword length in bits, minus the number of bits with which the * root table is indexed */ #define DECODE_TABLE_SYMBOL_SHIFT 4 #define DECODE_TABLE_MAX_SYMBOL ((1 << (16 - DECODE_TABLE_SYMBOL_SHIFT)) - 1) #define DECODE_TABLE_MAX_LENGTH ((1 << DECODE_TABLE_SYMBOL_SHIFT) - 1) #define DECODE_TABLE_LENGTH_MASK DECODE_TABLE_MAX_LENGTH #define MAKE_DECODE_TABLE_ENTRY(symbol, length) \ (((symbol) << DECODE_TABLE_SYMBOL_SHIFT) | (length)) /* * Read and return the next Huffman-encoded symbol from the given bitstream * using the given decode table. * * If the input data is exhausted, then the Huffman symbol will be decoded as if * the missing bits were all zeroes. * * XXX: This is mostly duplicated in lzms_decode_huffman_symbol() in * lzms_decompress.c; keep them in sync! */ static forceinline unsigned read_huffsym(struct input_bitstream *is, const u16 decode_table[], unsigned table_bits, unsigned max_codeword_len) { unsigned entry; unsigned symbol; unsigned length; /* Preload the bitbuffer with 'max_codeword_len' bits so that we're * guaranteed to be able to fully decode a codeword. */ bitstream_ensure_bits(is, max_codeword_len); /* Index the root table by the next 'table_bits' bits of input. */ entry = decode_table[bitstream_peek_bits(is, table_bits)]; /* Extract the "symbol" and "length" from the entry. */ symbol = entry >> DECODE_TABLE_SYMBOL_SHIFT; length = entry & DECODE_TABLE_LENGTH_MASK; /* If the root table is indexed by the full 'max_codeword_len' bits, * then there cannot be any subtables, and this will be known at compile * time. Otherwise, we must check whether the decoded symbol is really * a subtable pointer. If so, we must discard the bits with which the * root table was indexed, then index the subtable by the next 'length' * bits of input to get the real entry. */ if (max_codeword_len > table_bits && entry >= (1U << (table_bits + DECODE_TABLE_SYMBOL_SHIFT))) { /* Subtable required */ bitstream_remove_bits(is, table_bits); entry = decode_table[symbol + bitstream_peek_bits(is, length)]; symbol = entry >> DECODE_TABLE_SYMBOL_SHIFT; length = entry & DECODE_TABLE_LENGTH_MASK; } /* Discard the bits (or the remaining bits, if a subtable was required) * of the codeword. */ bitstream_remove_bits(is, length); /* Return the decoded symbol. */ return symbol; } /* * The DECODE_TABLE_ENOUGH() macro evaluates to the maximum number of decode * table entries, including all subtable entries, that may be required for * decoding a given Huffman code. This depends on three parameters: * * num_syms: the maximum number of symbols in the code * table_bits: the number of bits with which the root table will be indexed * max_codeword_len: the maximum allowed codeword length in the code * * Given these parameters, the utility program 'enough' from zlib, when passed * the three arguments 'num_syms', 'table_bits', and 'max_codeword_len', will * compute the maximum number of entries required. This has already been done * for the combinations we need and incorporated into the macro below so that * the mapping can be done at compilation time. If an unknown combination is * used, then a compilation error will result. To fix this, use 'enough' to * find the missing value and add it below. If that still doesn't fix the * compilation error, then most likely a constraint would be violated by the * requested parameters, so they cannot be used, at least without other changes * to the decode table --- see DECODE_TABLE_SIZE(). */ #define DECODE_TABLE_ENOUGH(num_syms, table_bits, max_codeword_len) ( \ ((num_syms) == 8 && (table_bits) == 7 && (max_codeword_len) == 15) ? 128 : \ ((num_syms) == 8 && (table_bits) == 5 && (max_codeword_len) == 7) ? 36 : \ ((num_syms) == 8 && (table_bits) == 6 && (max_codeword_len) == 7) ? 66 : \ ((num_syms) == 8 && (table_bits) == 7 && (max_codeword_len) == 7) ? 128 : \ ((num_syms) == 20 && (table_bits) == 5 && (max_codeword_len) == 15) ? 1062 : \ ((num_syms) == 20 && (table_bits) == 6 && (max_codeword_len) == 15) ? 582 : \ ((num_syms) == 20 && (table_bits) == 7 && (max_codeword_len) == 15) ? 390 : \ ((num_syms) == 54 && (table_bits) == 9 && (max_codeword_len) == 15) ? 618 : \ ((num_syms) == 54 && (table_bits) == 10 && (max_codeword_len) == 15) ? 1098 : \ ((num_syms) == 249 && (table_bits) == 9 && (max_codeword_len) == 16) ? 878 : \ ((num_syms) == 249 && (table_bits) == 10 && (max_codeword_len) == 16) ? 1326 : \ ((num_syms) == 249 && (table_bits) == 11 && (max_codeword_len) == 16) ? 2318 : \ ((num_syms) == 256 && (table_bits) == 9 && (max_codeword_len) == 15) ? 822 : \ ((num_syms) == 256 && (table_bits) == 10 && (max_codeword_len) == 15) ? 1302 : \ ((num_syms) == 256 && (table_bits) == 11 && (max_codeword_len) == 15) ? 2310 : \ ((num_syms) == 512 && (table_bits) == 10 && (max_codeword_len) == 15) ? 1558 : \ ((num_syms) == 512 && (table_bits) == 11 && (max_codeword_len) == 15) ? 2566 : \ ((num_syms) == 512 && (table_bits) == 12 && (max_codeword_len) == 15) ? 4606 : \ ((num_syms) == 656 && (table_bits) == 10 && (max_codeword_len) == 16) ? 1734 : \ ((num_syms) == 656 && (table_bits) == 11 && (max_codeword_len) == 16) ? 2726 : \ ((num_syms) == 656 && (table_bits) == 12 && (max_codeword_len) == 16) ? 4758 : \ ((num_syms) == 799 && (table_bits) == 9 && (max_codeword_len) == 15) ? 1366 : \ ((num_syms) == 799 && (table_bits) == 10 && (max_codeword_len) == 15) ? 1846 : \ ((num_syms) == 799 && (table_bits) == 11 && (max_codeword_len) == 15) ? 2854 : \ -1) /* Wrapper around DECODE_TABLE_ENOUGH() that does additional compile-time * validation. */ #define DECODE_TABLE_SIZE(num_syms, table_bits, max_codeword_len) ( \ \ /* All values must be positive. */ \ STATIC_ASSERT_ZERO((num_syms) > 0) + \ STATIC_ASSERT_ZERO((table_bits) > 0) + \ STATIC_ASSERT_ZERO((max_codeword_len) > 0) + \ \ /* There cannot be more symbols than possible codewords. */ \ STATIC_ASSERT_ZERO((num_syms) <= 1U << (max_codeword_len)) + \ \ /* There is no reason for the root table to be indexed with * more bits than the maximum codeword length. */ \ STATIC_ASSERT_ZERO((table_bits) <= (max_codeword_len)) + \ \ /* The maximum symbol value must fit in the 'symbol' field. */ \ STATIC_ASSERT_ZERO((num_syms) - 1 <= DECODE_TABLE_MAX_SYMBOL) + \ \ /* The maximum codeword length in the root table must fit in * the 'length' field. */ \ STATIC_ASSERT_ZERO((table_bits) <= DECODE_TABLE_MAX_LENGTH) + \ \ /* The maximum codeword length in a subtable must fit in the * 'length' field. */ \ STATIC_ASSERT_ZERO((max_codeword_len) - (table_bits) <= \ DECODE_TABLE_MAX_LENGTH) + \ \ /* The minimum subtable index must be greater than the maximum * symbol value. If this were not the case, then there would * be no way to tell whether a given root table entry is a * "subtable pointer" or not. (An alternate solution would be * to reserve a flag bit specifically for this purpose.) */ \ STATIC_ASSERT_ZERO((1U << table_bits) > (num_syms) - 1) + \ \ /* The needed 'enough' value must have been defined. */ \ STATIC_ASSERT_ZERO(DECODE_TABLE_ENOUGH( \ (num_syms), (table_bits), \ (max_codeword_len)) > 0) + \ \ /* The maximum subtable index must fit in the 'symbol' field. */\ STATIC_ASSERT_ZERO(DECODE_TABLE_ENOUGH( \ (num_syms), (table_bits), \ (max_codeword_len)) - 1 <= \ DECODE_TABLE_MAX_SYMBOL) + \ \ /* Finally, make the macro evaluate to the needed maximum * number of decode table entries. */ \ DECODE_TABLE_ENOUGH((num_syms), (table_bits), \ (max_codeword_len)) \ ) /* * Declare the decode table for a Huffman code, given several compile-time * constants that describe the code. See DECODE_TABLE_ENOUGH() for details. * * Decode tables must be aligned to a DECODE_TABLE_ALIGNMENT-byte boundary. * This implies that if a decode table is nested inside a dynamically allocated * structure, then the outer structure must be allocated on a * DECODE_TABLE_ALIGNMENT-byte aligned boundary as well. */ #define DECODE_TABLE(name, num_syms, table_bits, max_codeword_len) \ u16 name[DECODE_TABLE_SIZE((num_syms), (table_bits), \ (max_codeword_len))] \ _aligned_attribute(DECODE_TABLE_ALIGNMENT) /* * Declare the temporary "working_space" array needed for building the decode * table for a Huffman code. */ #define DECODE_TABLE_WORKING_SPACE(name, num_syms, max_codeword_len) \ u16 name[2 * ((max_codeword_len) + 1) + (num_syms)]; extern int make_huffman_decode_table(u16 decode_table[], unsigned num_syms, unsigned table_bits, const u8 lens[], unsigned max_codeword_len, u16 working_space[]); /******************************************************************************/ /* LZ match copying */ /*----------------------------------------------------------------------------*/ static forceinline void copy_word_unaligned(const void *src, void *dst) { store_word_unaligned(load_word_unaligned(src), dst); } static forceinline machine_word_t repeat_u16(u16 b) { machine_word_t v = b; STATIC_ASSERT(WORDBITS == 32 || WORDBITS == 64); v |= v << 16; v |= v << ((WORDBITS == 64) ? 32 : 0); return v; } static forceinline machine_word_t repeat_byte(u8 b) { return repeat_u16(((u16)b << 8) | b); } /* * Copy an LZ77 match of 'length' bytes from the match source at 'out_next - * offset' to the match destination at 'out_next'. The source and destination * may overlap. * * This handles validating the length and offset. It is validated that the * beginning of the match source is '>= out_begin' and that end of the match * destination is '<= out_end'. The return value is 0 if the match was valid * (and was copied), otherwise -1. * * 'min_length' is a hint which specifies the minimum possible match length. * This should be a compile-time constant. */ static forceinline int lz_copy(u32 length, u32 offset, u8 *out_begin, u8 *out_next, u8 *out_end, u32 min_length) { const u8 *src; u8 *end; /* Validate the offset. */ if (unlikely(offset > out_next - out_begin)) return -1; /* * Fast path: copy a match which is no longer than a few words, is not * overlapped such that copying a word at a time would produce incorrect * results, and is not too close to the end of the buffer. Note that * this might copy more than the length of the match, but that's okay in * this scenario. */ src = out_next - offset; if (UNALIGNED_ACCESS_IS_FAST && length <= 3 * WORDBYTES && offset >= WORDBYTES && out_end - out_next >= 3 * WORDBYTES) { copy_word_unaligned(src + WORDBYTES*0, out_next + WORDBYTES*0); copy_word_unaligned(src + WORDBYTES*1, out_next + WORDBYTES*1); copy_word_unaligned(src + WORDBYTES*2, out_next + WORDBYTES*2); return 0; } /* Validate the length. This isn't needed in the fast path above, due * to the additional conditions tested, but we do need it here. */ if (unlikely(length > out_end - out_next)) return -1; end = out_next + length; /* * Try to copy one word at a time. On i386 and x86_64 this is faster * than copying one byte at a time, unless the data is near-random and * all the matches have very short lengths. Note that since this * requires unaligned memory accesses, it won't necessarily be faster on * every architecture. * * Also note that we might copy more than the length of the match. For * example, if a word is 8 bytes and the match is of length 5, then * we'll simply copy 8 bytes. This is okay as long as we don't write * beyond the end of the output buffer, hence the check for (out_end - * end >= WORDBYTES - 1). */ if (UNALIGNED_ACCESS_IS_FAST && likely(out_end - end >= WORDBYTES - 1)) { if (offset >= WORDBYTES) { /* The source and destination words don't overlap. */ do { copy_word_unaligned(src, out_next); src += WORDBYTES; out_next += WORDBYTES; } while (out_next < end); return 0; } else if (offset == 1) { /* Offset 1 matches are equivalent to run-length * encoding of the previous byte. This case is common * if the data contains many repeated bytes. */ machine_word_t v = repeat_byte(*(out_next - 1)); do { store_word_unaligned(v, out_next); src += WORDBYTES; out_next += WORDBYTES; } while (out_next < end); return 0; } /* * We don't bother with special cases for other 'offset < * WORDBYTES', which are usually rarer than 'offset == 1'. * Extra checks will just slow things down. Actually, it's * possible to handle all the 'offset < WORDBYTES' cases using * the same code, but it still becomes more complicated doesn't * seem any faster overall; it definitely slows down the more * common 'offset == 1' case. */ } /* Fall back to a bytewise copy. */ if (min_length >= 2) *out_next++ = *src++; if (min_length >= 3) *out_next++ = *src++; if (min_length >= 4) *out_next++ = *src++; do { *out_next++ = *src++; } while (out_next != end); return 0; } #endif /* _WIMLIB_DECOMPRESS_COMMON_H */ wimlib-1.13.1/include/wimlib/avl_tree.h0000644000175000017500000002351113272231267014670 00000000000000/* * avl_tree.h - intrusive, nonrecursive AVL tree data structure (self-balancing * binary search tree), header file * * The following copying information applies to this specific source code file: * * Written in 2014-2016 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . */ #ifndef _AVL_TREE_H_ #define _AVL_TREE_H_ #include "wimlib/types.h" #define AVL_INLINE forceinline /* Node in an AVL tree. Embed this in some other data structure. */ struct avl_tree_node { /* Pointer to left child or NULL */ struct avl_tree_node *left; /* Pointer to right child or NULL */ struct avl_tree_node *right; /* Pointer to parent combined with the balance factor. This saves 4 or * 8 bytes of memory depending on the CPU architecture. * * Low 2 bits: One greater than the balance factor of this subtree, * which is equal to height(right) - height(left). The mapping is: * * 00 => -1 * 01 => 0 * 10 => +1 * 11 => undefined * * The rest of the bits are the pointer to the parent node. It must be * 4-byte aligned, and it will be NULL if this is the root node and * therefore has no parent. */ uintptr_t parent_balance; }; /* Cast an AVL tree node to the containing data structure. */ #define avl_tree_entry(entry, type, member) \ ((type*) ((char *)(entry) - offsetof(type, member))) /* Returns a pointer to the parent of the specified AVL tree node, or NULL if it * is already the root of the tree. */ static AVL_INLINE struct avl_tree_node * avl_get_parent(const struct avl_tree_node *node) { return (struct avl_tree_node *)(node->parent_balance & ~3); } /* (Internal use only) */ extern void avl_tree_rebalance_after_insert(struct avl_tree_node **root_ptr, struct avl_tree_node *inserted); /* * Looks up an item in the specified AVL tree. * * @root * Pointer to the root of the AVL tree. (This can be NULL --- that just * means the tree is empty.) * * @cmp_ctx * First argument to pass to the comparison callback. This generally * should be a pointer to an object equal to the one being searched for. * * @cmp * Comparison callback. Must return < 0, 0, or > 0 if the first argument * is less than, equal to, or greater than the second argument, * respectively. The first argument will be @cmp_ctx and the second * argument will be a pointer to the AVL tree node of an item in the tree. * * Returns a pointer to the AVL tree node of the resulting item, or NULL if the * item was not found. * * Example: * * struct int_wrapper { * int data; * struct avl_tree_node index_node; * }; * * static int _avl_cmp_int_to_node(const void *intptr, * const struct avl_tree_node *nodeptr) * { * int n1 = *(const int *)intptr; * int n2 = avl_tree_entry(nodeptr, struct int_wrapper, index_node)->data; * if (n1 < n2) * return -1; * else if (n1 > n2) * return 1; * else * return 0; * } * * bool contains_int(struct avl_tree_node *root, int n) * { * struct avl_tree_node *result; * * result = avl_tree_lookup(root, &n, _avl_cmp_int_to_node); * return result ? true : false; * } */ static AVL_INLINE struct avl_tree_node * avl_tree_lookup(const struct avl_tree_node *root, const void *cmp_ctx, int (*cmp)(const void *, const struct avl_tree_node *)) { const struct avl_tree_node *cur = root; while (cur) { int res = (*cmp)(cmp_ctx, cur); if (res < 0) cur = cur->left; else if (res > 0) cur = cur->right; else break; } return (struct avl_tree_node*)cur; } /* Same as avl_tree_lookup(), but uses a more specific type for the comparison * function. Specifically, with this function the item being searched for is * expected to be in the same format as those already in the tree, with an * embedded 'struct avl_tree_node'. */ static AVL_INLINE struct avl_tree_node * avl_tree_lookup_node(const struct avl_tree_node *root, const struct avl_tree_node *node, int (*cmp)(const struct avl_tree_node *, const struct avl_tree_node *)) { const struct avl_tree_node *cur = root; while (cur) { int res = (*cmp)(node, cur); if (res < 0) cur = cur->left; else if (res > 0) cur = cur->right; else break; } return (struct avl_tree_node*)cur; } /* * Inserts an item into the specified AVL tree. * * @root_ptr * Location of the AVL tree's root pointer. Indirection is needed because * the root node may change as a result of rotations caused by the * insertion. Initialize *root_ptr to NULL for an empty tree. * * @item * Pointer to the `struct avl_tree_node' embedded in the item to insert. * No members in it need be pre-initialized, although members in the * containing structure should be pre-initialized so that @cmp can use them * in comparisons. * * @cmp * Comparison callback. Must return < 0, 0, or > 0 if the first argument * is less than, equal to, or greater than the second argument, * respectively. The first argument will be @item and the second * argument will be a pointer to an AVL tree node embedded in some * previously-inserted item to which @item is being compared. * * If no item in the tree is comparatively equal (via @cmp) to @item, inserts * @item and returns NULL. Otherwise does nothing and returns a pointer to the * AVL tree node embedded in the previously-inserted item which compared equal * to @item. * * Example: * * struct int_wrapper { * int data; * struct avl_tree_node index_node; * }; * * #define GET_DATA(i) avl_tree_entry((i), struct int_wrapper, index_node)->data * * static int _avl_cmp_ints(const struct avl_tree_node *node1, * const struct avl_tree_node *node2) * { * int n1 = GET_DATA(node1); * int n2 = GET_DATA(node2); * if (n1 < n2) * return -1; * else if (n1 > n2) * return 1; * else * return 0; * } * * bool insert_int(struct avl_tree_node **root_ptr, int data) * { * struct int_wrapper *i = malloc(sizeof(struct int_wrapper)); * i->data = data; * if (avl_tree_insert(root_ptr, &i->index_node, _avl_cmp_ints)) { * // Duplicate. * free(i); * return false; * } * return true; * } */ static AVL_INLINE struct avl_tree_node * avl_tree_insert(struct avl_tree_node **root_ptr, struct avl_tree_node *item, int (*cmp)(const struct avl_tree_node *, const struct avl_tree_node *)) { struct avl_tree_node **cur_ptr = root_ptr, *cur = NULL; int res; while (*cur_ptr) { cur = *cur_ptr; res = (*cmp)(item, cur); if (res < 0) cur_ptr = &cur->left; else if (res > 0) cur_ptr = &cur->right; else return cur; } *cur_ptr = item; item->parent_balance = (uintptr_t)cur | 1; avl_tree_rebalance_after_insert(root_ptr, item); return NULL; } /* Removes an item from the specified AVL tree. * See implementation for details. */ extern void avl_tree_remove(struct avl_tree_node **root_ptr, struct avl_tree_node *node); /* Nonrecursive AVL tree traversal functions */ extern struct avl_tree_node * avl_tree_first_in_order(const struct avl_tree_node *root); extern struct avl_tree_node * avl_tree_last_in_order(const struct avl_tree_node *root); extern struct avl_tree_node * avl_tree_next_in_order(const struct avl_tree_node *node); extern struct avl_tree_node * avl_tree_prev_in_order(const struct avl_tree_node *node); extern struct avl_tree_node * avl_tree_first_in_postorder(const struct avl_tree_node *root); extern struct avl_tree_node * avl_tree_next_in_postorder(const struct avl_tree_node *prev, const struct avl_tree_node *prev_parent); /* * Iterate through the nodes in an AVL tree in sorted order. * You may not modify the tree during the iteration. * * @child_struct * Variable that will receive a pointer to each struct inserted into the * tree. * @root * Root of the AVL tree. * @struct_name * Type of *child_struct. * @struct_member * Member of @struct_name type that is the AVL tree node. * * Example: * * struct int_wrapper { * int data; * struct avl_tree_node index_node; * }; * * void print_ints(struct avl_tree_node *root) * { * struct int_wrapper *i; * * avl_tree_for_each_in_order(i, root, struct int_wrapper, index_node) * printf("%d\n", i->data); * } */ #define avl_tree_for_each_in_order(child_struct, root, \ struct_name, struct_member) \ for (struct avl_tree_node *_cur = \ avl_tree_first_in_order(root); \ _cur && ((child_struct) = \ avl_tree_entry(_cur, struct_name, \ struct_member), 1); \ _cur = avl_tree_next_in_order(_cur)) /* * Like avl_tree_for_each_in_order(), but uses the reverse order. */ #define avl_tree_for_each_in_reverse_order(child_struct, root, \ struct_name, struct_member) \ for (struct avl_tree_node *_cur = \ avl_tree_last_in_order(root); \ _cur && ((child_struct) = \ avl_tree_entry(_cur, struct_name, \ struct_member), 1); \ _cur = avl_tree_prev_in_order(_cur)) /* * Like avl_tree_for_each_in_order(), but iterates through the nodes in * postorder, so the current node may be deleted or freed. */ #define avl_tree_for_each_in_postorder(child_struct, root, \ struct_name, struct_member) \ for (struct avl_tree_node *_cur = \ avl_tree_first_in_postorder(root), *_parent; \ _cur && ((child_struct) = \ avl_tree_entry(_cur, struct_name, \ struct_member), 1) \ && (_parent = avl_get_parent(_cur), 1); \ _cur = avl_tree_next_in_postorder(_cur, _parent)) #endif /* _AVL_TREE_H_ */ wimlib-1.13.1/include/wimlib/paths.h0000644000175000017500000000247013126627466014217 00000000000000#ifndef _WIMLIB_PATHS_H #define _WIMLIB_PATHS_H #include "wimlib/compiler.h" #include "wimlib/types.h" const tchar * path_basename(const tchar *path); const tchar * path_basename_with_len(const tchar *path, size_t len); extern const tchar * path_stream_name(const tchar *path); extern void do_canonicalize_path(const tchar *in, tchar *out); extern tchar * canonicalize_wim_path(const tchar *wim_path) _malloc_attribute; /* is_any_path_separator() - characters treated as path separators in WIM path * specifications and capture configuration files (the former will be translated * to WIM_PATH_SEPARATOR; the latter will be translated to * OS_PREFERRED_PATH_SEPARATOR) * * OS_PREFERRED_PATH_SEPARATOR - preferred (or only) path separator on the * operating system. Used when constructing filesystem paths to extract or * archive. * * WIM_PATH_SEPARATOR - character treated as path separator for WIM paths. * Currently needs to be '/' on UNIX for the WIM mounting code to work properly. */ #ifdef __WIN32__ # define OS_PREFERRED_PATH_SEPARATOR L'\\' # define is_any_path_separator(c) ((c) == L'/' || (c) == L'\\') #else # define OS_PREFERRED_PATH_SEPARATOR '/' # define is_any_path_separator(c) ((c) == '/' || (c) == '\\') #endif #define WIM_PATH_SEPARATOR WIMLIB_WIM_PATH_SEPARATOR #endif /* _WIMLIB_PATHS_H */ wimlib-1.13.1/include/wimlib/unaligned.h0000644000175000017500000000750313272231267015040 00000000000000/* * unaligned.h - inline functions for unaligned memory accesses * * The following copying information applies to this specific source code file: * * Written in 2014-2015 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . */ #ifndef _WIMLIB_UNALIGNED_H #define _WIMLIB_UNALIGNED_H #include "wimlib/compiler.h" #include "wimlib/endianness.h" #include "wimlib/types.h" #define DEFINE_UNALIGNED_TYPE(type) \ struct type##_unaligned { \ type v; \ } _packed_attribute _may_alias_attribute; \ \ static forceinline type \ load_##type##_unaligned(const void *p) \ { \ return ((const struct type##_unaligned *)p)->v; \ } \ \ static forceinline void \ store_##type##_unaligned(type val, void *p) \ { \ ((struct type##_unaligned *)p)->v = val; \ } DEFINE_UNALIGNED_TYPE(u16); DEFINE_UNALIGNED_TYPE(u32); DEFINE_UNALIGNED_TYPE(u64); DEFINE_UNALIGNED_TYPE(le16); DEFINE_UNALIGNED_TYPE(le32); DEFINE_UNALIGNED_TYPE(le64); DEFINE_UNALIGNED_TYPE(be16); DEFINE_UNALIGNED_TYPE(be32); DEFINE_UNALIGNED_TYPE(be64); DEFINE_UNALIGNED_TYPE(size_t); DEFINE_UNALIGNED_TYPE(machine_word_t); #define load_word_unaligned load_machine_word_t_unaligned #define store_word_unaligned store_machine_word_t_unaligned static forceinline u16 get_unaligned_le16(const u8 *p) { if (UNALIGNED_ACCESS_IS_FAST) return le16_to_cpu(load_le16_unaligned(p)); else return ((u16)p[1] << 8) | p[0]; } static forceinline u32 get_unaligned_le32(const u8 *p) { if (UNALIGNED_ACCESS_IS_FAST) return le32_to_cpu(load_le32_unaligned(p)); else return ((u32)p[3] << 24) | ((u32)p[2] << 16) | ((u32)p[1] << 8) | p[0]; } static forceinline void put_unaligned_le16(u16 v, u8 *p) { if (UNALIGNED_ACCESS_IS_FAST) { store_le16_unaligned(cpu_to_le16(v), p); } else { p[0] = (u8)(v >> 0); p[1] = (u8)(v >> 8); } } static forceinline void put_unaligned_le32(u32 v, u8 *p) { if (UNALIGNED_ACCESS_IS_FAST) { store_le32_unaligned(cpu_to_le32(v), p); } else { p[0] = (u8)(v >> 0); p[1] = (u8)(v >> 8); p[2] = (u8)(v >> 16); p[3] = (u8)(v >> 24); } } /* * Given a 32-bit value that was loaded with the platform's native endianness, * return a 32-bit value whose high-order 8 bits are 0 and whose low-order 24 * bits contain the first 3 bytes, arranged in octets in a platform-dependent * order, at the memory location from which the input 32-bit value was loaded. */ static forceinline u32 loaded_u32_to_u24(u32 v) { if (CPU_IS_LITTLE_ENDIAN) return v & 0xFFFFFF; else return v >> 8; } /* * Load the next 3 bytes from the memory location @p into the 24 low-order bits * of a 32-bit value. The order in which the 3 bytes will be arranged as octets * in the 24 bits is platform-dependent. At least LOAD_U24_REQUIRED_NBYTES * bytes must be available at @p; note that this may be more than 3. */ static forceinline u32 load_u24_unaligned(const u8 *p) { #if UNALIGNED_ACCESS_IS_FAST # define LOAD_U24_REQUIRED_NBYTES 4 return loaded_u32_to_u24(load_u32_unaligned(p)); #else # define LOAD_U24_REQUIRED_NBYTES 3 # if CPU_IS_BIG_ENDIAN return ((u32)p[2] << 0) | ((u32)p[1] << 8) | ((u32)p[0] << 16); # else return ((u32)p[0] << 0) | ((u32)p[1] << 8) | ((u32)p[2] << 16); # endif #endif } #endif /* _WIMLIB_UNALIGNED_H */ wimlib-1.13.1/include/wimlib/scan.h0000644000175000017500000001015213324657474014022 00000000000000#ifndef _WIMLIB_SCAN_H #define _WIMLIB_SCAN_H #include "wimlib.h" #include "wimlib/inode_table.h" #include "wimlib/list.h" #include "wimlib/progress.h" #include "wimlib/security.h" #include "wimlib/textfile.h" #include "wimlib/util.h" struct blob_table; struct wim_dentry; struct wim_inode; struct capture_config { /* List of path patterns to exclude */ struct string_list exclusion_pats; /* List of path patterns to include, overriding exclusion_pats */ struct string_list exclusion_exception_pats; void *buf; }; /* Scan parameters: common parameters to implementations of building an * in-memory dentry tree from an external directory structure. */ struct scan_params { /* The blob table within which any new blobs discovered during the scan * will be deduplicated. */ struct blob_table *blob_table; /* List of new blobs that have been discovered without their SHA-1 * message digests having been calculated (as a shortcut). */ struct list_head *unhashed_blobs; /* Map from (inode number, device number) pair to inode for new inodes * that have been discovered so far. */ struct wim_inode_table *inode_table; /* The set of unique security descriptors to which each newly * discovered, unique security descriptor will be added. */ struct wim_sd_set *sd_set; /* The capture configuration in effect, or NULL if none. */ struct capture_config *config; /* Flags that affect the scan operation (WIMLIB_ADD_FLAG_*) */ int add_flags; /* If non-NULL, the user-supplied progress function. */ wimlib_progress_func_t progfunc; void *progctx; /* Progress data. */ union wimlib_progress_info progress; /* Path to the file or directory currently being scanned */ tchar *cur_path; size_t cur_path_nchars; size_t cur_path_alloc_nchars; /* Length of the prefix of 'cur_path' which names the root of the * directory tree currently being scanned */ size_t root_path_nchars; /* Can be used by the scan implementation. */ u64 capture_root_ino; u64 capture_root_dev; }; /* scan.c */ extern int do_scan_progress(struct scan_params *params, int status, const struct wim_inode *inode); extern int mangle_pat(tchar *pat, const tchar *path, unsigned long line_no); extern int read_capture_config(const tchar *config_file, const void *buf, size_t bufsize, struct capture_config *config); extern void destroy_capture_config(struct capture_config *config); extern bool match_pattern_list(const tchar *path, const struct string_list *list, int match_flags); extern int try_exclude(const struct scan_params *params); typedef int (*scan_tree_t)(struct wim_dentry **, const tchar *, struct scan_params *); #ifdef WITH_NTFS_3G /* ntfs-3g_capture.c */ extern int ntfs_3g_build_dentry_tree(struct wim_dentry **root_ret, const tchar *device, struct scan_params *params); #endif #ifdef __WIN32__ /* win32_capture.c */ extern int win32_build_dentry_tree(struct wim_dentry **root_ret, const tchar *root_disk_path, struct scan_params *params); #define platform_default_scan_tree win32_build_dentry_tree #else /* unix_capture.c */ extern int unix_build_dentry_tree(struct wim_dentry **root_ret, const tchar *root_disk_path, struct scan_params *params); #define platform_default_scan_tree unix_build_dentry_tree #endif #ifdef ENABLE_TEST_SUPPORT extern int generate_dentry_tree(struct wim_dentry **root_ret, const tchar *root_disk_path, struct scan_params *params); #endif #define WIMLIB_ADD_FLAG_ROOT 0x80000000 static inline int report_scan_error(struct scan_params *params, int error_code) { return report_error(params->progfunc, params->progctx, error_code, params->cur_path); } extern bool should_ignore_filename(const tchar *name, int name_nchars); extern void attach_scanned_tree(struct wim_dentry *parent, struct wim_dentry *child, struct blob_table *blob_table); extern int pathbuf_init(struct scan_params *params, const tchar *root_path); extern const tchar * pathbuf_append_name(struct scan_params *params, const tchar *name, size_t name_nchars, size_t *orig_path_nchars_ret); extern void pathbuf_truncate(struct scan_params *params, size_t nchars); #endif /* _WIMLIB_SCAN_H */ wimlib-1.13.1/include/wimlib/win32_vss.h0000644000175000017500000000214713160354224014721 00000000000000/* * win32_vss.h - Declarations for managing VSS snapshots. This header should * only be included by Windows-specific files. */ #ifndef _WIMLIB_WIN32_VSS_H #define _WIMLIB_WIN32_VSS_H #include "wimlib/win32_common.h" /* A reference counter for a VSS snapshot. This is embedded in another data * structure only visible to win32_vss.c. */ struct vss_snapshot { size_t refcnt; }; extern void vss_delete_snapshot(struct vss_snapshot *snapshot); /* Acquire a reference to the specified VSS snapshot. */ static inline struct vss_snapshot * vss_get_snapshot(struct vss_snapshot *snapshot) { if (snapshot) snapshot->refcnt++; return snapshot; } /* Release a reference to the specified VSS snapshot. When the last reference * is released, the snapshot is deleted. */ static inline void vss_put_snapshot(struct vss_snapshot *snapshot) { if (snapshot && --snapshot->refcnt == 0) vss_delete_snapshot(snapshot); } extern int vss_create_snapshot(const wchar_t *source, UNICODE_STRING *vss_path_ret, struct vss_snapshot **snapshot_ret); extern void vss_global_cleanup(void); #endif /* _WIMLIB_WIN32_VSS_H */ wimlib-1.13.1/include/wimlib/wimboot.h0000644000175000017500000000111313160354224014534 00000000000000#ifndef _WIMBOOT_H_ #define _WIMBOOT_H_ #include "wimlib/header.h" #include "wimlib/sha1.h" #include "wimlib/types.h" #include "wimlib/win32_common.h" struct blob_descriptor; extern int wimboot_alloc_data_source_id(const wchar_t *wim_path, const u8 guid[GUID_SIZE], int image, const wchar_t *target, u64 *data_source_id_ret, bool *wof_running_ret); extern bool wimboot_set_pointer(HANDLE h, const struct blob_descriptor *blob, u64 data_source_id, const u8 blob_table_hash[SHA1_HASH_SIZE], bool wof_running); #endif /* _WIMBOOT_H_ */ wimlib-1.13.1/include/wimlib/types.h0000644000175000017500000000252613160354224014231 00000000000000#ifndef _WIMLIB_TYPES_H #define _WIMLIB_TYPES_H #include #include #include #include "wimlib_tchar.h" #include "wimlib/compiler.h" #ifndef _NTFS_TYPES_H /* Unsigned integer types of exact size in bits */ typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; /* Signed integer types of exact size in bits */ typedef int8_t s8; typedef int16_t s16; typedef int32_t s32; typedef int64_t s64; /* Unsigned little endian types of exact size */ typedef uint16_t _bitwise_attr le16; typedef uint32_t _bitwise_attr le32; typedef uint64_t _bitwise_attr le64; /* Unsigned big endian types of exact size */ typedef uint16_t _bitwise_attr be16; typedef uint32_t _bitwise_attr be32; typedef uint64_t _bitwise_attr be64; #endif /* A pointer to 'utf16lechar' indicates a UTF-16LE encoded string */ typedef le16 utf16lechar; #ifndef WIMLIB_WIMSTRUCT_DECLARED typedef struct WIMStruct WIMStruct; # define WIMLIB_WIMSTRUCT_DECLARED #endif /* * Type of a machine word. 'unsigned long' would be logical, but that is only * 32 bits on x86_64 Windows. The same applies to 'uint_fast32_t'. So the best * we can do without a bunch of #ifdefs appears to be 'size_t'. */ typedef size_t machine_word_t; #define WORDBYTES sizeof(machine_word_t) #define WORDBITS (8 * WORDBYTES) #endif /* _WIMLIB_TYPES_H */ wimlib-1.13.1/include/wimlib/win32_common.h0000644000175000017500000001062013272231267015376 00000000000000/* * win32_common.h - common header for Windows-specific files. This always * should be included first. */ #ifndef _WIMLIB_WIN32_COMMON_H #define _WIMLIB_WIN32_COMMON_H #include #include #include #ifdef ERROR # undef ERROR #endif #include "wimlib/win32.h" /* ntdll definitions */ #define FILE_OPENED 0x00000001 typedef struct _RTLP_CURDIR_REF { LONG RefCount; HANDLE Handle; } RTLP_CURDIR_REF, *PRTLP_CURDIR_REF; typedef struct _RTL_RELATIVE_NAME_U { UNICODE_STRING RelativeName; HANDLE ContainingDirectory; PRTLP_CURDIR_REF CurDirRef; } RTL_RELATIVE_NAME_U, *PRTL_RELATIVE_NAME_U; #define FSCTL_SET_PERSISTENT_VOLUME_STATE 0x90238 #define PERSISTENT_VOLUME_STATE_SHORT_NAME_CREATION_DISABLED 0x00000001 typedef struct _FILE_FS_PERSISTENT_VOLUME_INFORMATION { ULONG VolumeFlags; ULONG FlagMask; ULONG Version; ULONG Reserved; } FILE_FS_PERSISTENT_VOLUME_INFORMATION, *PFILE_FS_PERSISTENT_VOLUME_INFORMATION; /* ntdll functions */ NTSTATUS NTAPI NtReadFile(IN HANDLE FileHandle, IN HANDLE Event OPTIONAL, IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, IN PVOID ApcContext OPTIONAL, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID Buffer, IN ULONG Length, IN PLARGE_INTEGER ByteOffset OPTIONAL, IN PULONG Key OPTIONAL); NTSTATUS NTAPI NtWriteFile(IN HANDLE FileHandle, IN HANDLE Event OPTIONAL, IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, IN PVOID ApcContext OPTIONAL, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PVOID Buffer, IN ULONG Length, IN PLARGE_INTEGER ByteOffset OPTIONAL, IN PULONG Key OPTIONAL); NTSTATUS NTAPI NtQueryDirectoryFile(IN HANDLE FileHandle, IN HANDLE EventHandle OPTIONAL, IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, IN PVOID ApcContext OPTIONAL, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FileInformation, IN ULONG Length, IN FILE_INFORMATION_CLASS FileInformationClass, IN BOOLEAN ReturnSingleEntry, IN PUNICODE_STRING FileName OPTIONAL, IN BOOLEAN RestartScan); NTSTATUS NTAPI NtQuerySecurityObject(IN HANDLE Handle, IN SECURITY_INFORMATION SecurityInformation, OUT PSECURITY_DESCRIPTOR SecurityDescriptor, IN ULONG Length, OUT PULONG ResultLength); NTSTATUS NTAPI NtSetSecurityObject(IN HANDLE Handle, IN SECURITY_INFORMATION SecurityInformation, IN PSECURITY_DESCRIPTOR SecurityDescriptor); NTSTATUS NTAPI NtOpenSymbolicLinkObject(PHANDLE LinkHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes); NTSTATUS NTAPI NtQueryEaFile(IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID Buffer, IN ULONG Length, IN BOOLEAN ReturnSingleEntry, IN PVOID EaList OPTIONAL, IN ULONG EaListLength, IN PULONG EaIndex OPTIONAL, IN BOOLEAN RestartScan); NTSTATUS NTAPI NtSetEaFile(IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID Buffer, IN ULONG Length); /* Dynamically loaded ntdll functions */ extern NTSTATUS (WINAPI *func_RtlDosPathNameToNtPathName_U_WithStatus) (IN PCWSTR DosName, OUT PUNICODE_STRING NtName, OUT PCWSTR *PartName, OUT PRTL_RELATIVE_NAME_U RelativeName); extern NTSTATUS (WINAPI *func_RtlCreateSystemVolumeInformationFolder) (PCUNICODE_STRING VolumeRootPath); /* Other utility functions */ extern int win32_path_to_nt_path(const wchar_t *win32_path, UNICODE_STRING *nt_path); extern int win32_get_drive_path(const wchar_t *file_path, wchar_t drive_path[7]); extern bool win32_try_to_attach_wof(const wchar_t *drive); extern void win32_warning(DWORD err, const wchar_t *format, ...) _cold_attribute; extern void win32_error(DWORD err, const wchar_t *format, ...) _cold_attribute; extern void winnt_warning(NTSTATUS status, const wchar_t *format, ...) _cold_attribute; extern void winnt_error(NTSTATUS status, const wchar_t *format, ...) _cold_attribute; extern NTSTATUS winnt_fsctl(HANDLE h, u32 code, const void *in, u32 in_size, void *out, u32 out_size_avail, u32 *actual_out_size_ret); #endif /* _WIMLIB_WIN32_COMMON_H */ wimlib-1.13.1/include/wimlib/textfile.h0000644000175000017500000000167113324671725014723 00000000000000#ifndef _WIMLIB_TEXTFILE_H_ #define _WIMLIB_TEXTFILE_H_ #include "wimlib/types.h" struct string_list { tchar **strings; size_t num_strings; size_t num_alloc_strings; }; #define STRING_LIST_INITIALIZER \ { .strings = NULL, .num_strings = 0, .num_alloc_strings = 0, } #define STRING_LIST(_strings) \ struct string_list _strings = STRING_LIST_INITIALIZER typedef int (*line_mangle_t)(tchar *line, const tchar *filename, unsigned long line_no); struct text_file_section { const tchar *name; struct string_list *strings; }; #define LOAD_TEXT_FILE_REMOVE_QUOTES 0x00000001 #define LOAD_TEXT_FILE_NO_WARNINGS 0x00000002 #define LOAD_TEXT_FILE_ALLOW_STDIN 0x00000004 extern int load_text_file(const tchar *path, const void *buf, size_t bufsize, void **mem_ret, const struct text_file_section *pos_sections, int num_pos_sections, int flags, line_mangle_t mangle_line); #endif /* _WIMLIB_TEXTFILE_H_ */ wimlib-1.13.1/include/wimlib/registry.h0000644000175000017500000000160613160354224014733 00000000000000#ifndef _WIMLIB_REGISTRY_H #define _WIMLIB_REGISTRY_H #include "wimlib/types.h" struct regf; enum hive_status { HIVE_OK, HIVE_CORRUPT, HIVE_UNSUPPORTED, HIVE_KEY_NOT_FOUND, HIVE_VALUE_NOT_FOUND, HIVE_VALUE_IS_WRONG_TYPE, HIVE_OUT_OF_MEMORY, HIVE_ITERATION_STOPPED, }; extern enum hive_status hive_validate(const void *hive_mem, size_t hive_size); extern enum hive_status hive_get_string(const struct regf *regf, const tchar *key_name, const tchar *value_name, tchar **value_ret); extern enum hive_status hive_get_number(const struct regf *regf, const tchar *key_name, const tchar *value_name, s64 *value_ret); extern enum hive_status hive_list_subkeys(const struct regf *regf, const tchar *key_name, tchar ***subkeys_ret); extern void hive_free_subkeys_list(tchar **subkeys); extern const char * hive_status_to_string(enum hive_status status); #endif /* _WIMLIB_REGISTRY_H */ wimlib-1.13.1/include/wimlib/guid.h0000644000175000017500000000130613160354224014010 00000000000000/* * guid.h * * Utility functions for handling 16-byte globally unique identifiers (GUIDs). */ #ifndef _WIMLIB_GUID_H #define _WIMLIB_GUID_H #include #include "wimlib/util.h" #define GUID_SIZE 16 static inline void copy_guid(u8 dest[GUID_SIZE], const u8 src[GUID_SIZE]) { memcpy(dest, src, GUID_SIZE); } static inline int cmp_guids(const u8 guid1[GUID_SIZE], const u8 guid2[GUID_SIZE]) { return memcmp(guid1, guid2, GUID_SIZE); } static inline bool guids_equal(const u8 guid1[GUID_SIZE], const u8 guid2[GUID_SIZE]) { return !cmp_guids(guid1, guid2); } static inline void generate_guid(u8 guid[GUID_SIZE]) { return get_random_bytes(guid, GUID_SIZE); } #endif /* _WIMLIB_GUID_H */ wimlib-1.13.1/include/wimlib/unix_data.h0000644000175000017500000000116313030246726015040 00000000000000#ifndef _WIMLIB_UNIX_DATA_H #define _WIMLIB_UNIX_DATA_H #include "wimlib/types.h" struct wimlib_unix_data { u32 uid; u32 gid; u32 mode; u32 rdev; }; struct wim_inode; extern bool inode_has_unix_data(const struct wim_inode *inode); extern bool inode_get_unix_data(const struct wim_inode *inode, struct wimlib_unix_data *unix_data); #define UNIX_DATA_UID 0x1 #define UNIX_DATA_GID 0x2 #define UNIX_DATA_MODE 0x4 #define UNIX_DATA_RDEV 0x8 #define UNIX_DATA_ALL 0xF extern bool inode_set_unix_data(struct wim_inode *inode, struct wimlib_unix_data *unix_data, int which); #endif /* _WIMLIB_UNIX_DATA_H */ wimlib-1.13.1/include/wimlib/wof.h0000644000175000017500000004271213160354224013661 00000000000000/* * wof.h * * Definitions for the Windows Overlay Filesystem filter (WOF) ioctls, as well * some definitions for associated undocumented data structures. See * http://msdn.microsoft.com/en-us/library/windows/hardware/ff540367(v=vs.85).aspx * for more information about the documented ioctls. * * The following copying information applies to this specific source code file: * * Written in 2014-2016 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . */ #ifndef _WOF_H_ #define _WOF_H_ #include "wimlib/compiler.h" #include "wimlib/types.h" /* * The Windows Overlay Filesystem filter (WOF, a.k.a. wof.sys) is a filesystem * filter driver, available in Windows 8.1 and later, which allows files to be * "externally backed", meaning that their data is stored in another location, * possibly in compressed form. * * WOF implements a plug-in mechanism by which a specific "provider" is * responsible for actually externally backing a given file. The currently * known providers are: * * - The WIM provider: allows a file to be externally backed by a * compressed resource in a WIM archive * - The file provider: allows a file to be "externally backed" by a named * data stream stored with the file itself, where that named data stream * has the format of a compressed WIM resource * * For both of these providers, externally backed files are effectively * read-only. If you try to write to such a file, Windows automatically * decompresses it and turns it into a regular, non-externally-backed file. * * WOF provides various ioctls that control its operation. For example, * FSCTL_SET_EXTERNAL_BACKING sets up a file as externally backed. * * WOF external backings are implemented using reparse points. One consequence * of this is that WOF external backings can only be set on files that do not * already have a reparse point set. Another consequence of this is that it is * possible to create a WOF external backing by manually creating the reparse * point, although this requires dealing with undocumented data structures and * it only works when the WOF driver is not currently attached to the volume. * * Note that only the unnamed data stream portion of a file can be externally * backed. Other NTFS streams and metadata are not externally backed. */ /* Current version of the WOF driver/protocol */ #define WOF_CURRENT_VERSION 1 /* Specifies the WIM backing provider */ #define WOF_PROVIDER_WIM 1 /* Specifies the "file" backing provider (a.k.a. System Compression) */ #define WOF_PROVIDER_FILE 2 /* The current version of the WIM backing provider */ #define WIM_PROVIDER_CURRENT_VERSION 1 /* The current version of the file backing provider */ #define FILE_PROVIDER_CURRENT_VERSION 1 /* Identifies a backing provider for a specific overlay service version. */ struct wof_external_info { /* Version of the overlay service supported by the backing provider. * Set to WOF_CURRENT_VERSION. */ u32 version; /* Identifier for the backing provider. Example value: * WOF_PROVIDER_WIM. */ u32 provider; }; /* * Format of the WIM provider reparse data. This is the data which follows the * portion of the reparse point common to WOF. (The common portion consists of * a reparse point header where the reparse tag is 0x80000017, then a 'struct * wof_external_info' which specifies the provider.) * * Note that Microsoft does not document any of the reparse point formats for * WOF, although they document the structures which must be passed into the * ioctls, which are often similar. */ struct wim_provider_rpdata { /* Set to 2. Uncertain meaning. */ le32 version; /* 0 when WIM provider active, otherwise * WIM_PROVIDER_EXTERNAL_FLAG_NOT_ACTIVE or * WIM_PROVIDER_EXTERNAL_FLAG_SUSPENDED. */ le32 flags; /* Integer ID that identifies the WIM. */ le64 data_source_id; /* SHA-1 message digest of the file's unnamed data stream. */ u8 unnamed_data_stream_hash[20]; /* SHA-1 message digest of the WIM's blob table as stored on disk. */ u8 blob_table_hash[20]; /* Uncompressed size of the file's unnamed data stream, in bytes. */ le64 unnamed_data_stream_size; /* Size of the file's unnamed data stream as stored in the WIM file. * If this is the same as unnamed_data_stream_size, then the stream is * uncompressed. If this is the *not* the same as * unnamed_data_stream_size, then the stream is compressed. */ le64 unnamed_data_stream_size_in_wim; /* Byte offset of the file's unnamed data stream in the WIM. */ le64 unnamed_data_stream_offset_in_wim; } _packed_attribute; /* WIM-specific information about a WIM data source */ struct WimOverlay_dat_entry_1 { /* Identifier for the WIM data source, (normally allocated by * FSCTL_ADD_OVERLAY). Every 'WimOverlay_dat_entry_1' should have a * different value for this. */ le64 data_source_id; /* Byte offset, from the beginning of the file, of the corresponding * 'struct WimOverlay_dat_entry_2' for this WIM data source. */ le32 entry_2_offset; /* Size, in bytes, of the corresponding 'struct WimOverlay_dat_entry_2 * for this WIM data source, including wim_file_name and its null * terminator. */ le32 entry_2_length; /* Type of the WIM file: WIM_BOOT_OS_WIM or WIM_BOOT_NOT_OS_WIM. */ le32 wim_type; /* Index of the image in the WIM to use??? (This doesn't really make * sense, since WIM files combine file data "blobs" for all images into * a single table. Set to 1 if unsure...) */ le32 wim_index; /* GUID of the WIM file (copied from the WIM header, offset +0x18). */ u8 guid[16]; } _packed_attribute; /* * Format of file: "\System Volume Information\WimOverlay.dat" * * Not documented by Microsoft. * * The file consists of a 'struct WimOverlay_dat_header' followed by one or more * 'struct WimOverlay_dat_entry_1', followed by the same number of 'struct * WimOverlay_dat_entry_2'. Note that 'struct WimOverlay_dat_entry_1' is of * fixed length, whereas 'struct WimOverlay_dat_entry_2' is of variable length. */ struct WimOverlay_dat_header { /* Set to WIMOVERLAY_DAT_MAGIC */ le32 magic; #define WIMOVERLAY_DAT_MAGIC 0x66436F57 /* Set to 1 (WIM_PROVIDER_CURRENT_VERSION) */ le32 wim_provider_version; /* Set to 0x00000028 */ le32 unknown_0x08; /* Set to number of WIMs registered (listed in the file) */ le32 num_entries; /* The next available data source ID. This is tracked so that data * source IDs are never reused, even if a WIM is unregistered. */ le64 next_data_source_id; struct WimOverlay_dat_entry_1 entry_1s[]; } _packed_attribute; /* Location information about a WIM data source */ struct WimOverlay_dat_entry_2 { /* Set to 0 */ le32 unknown_0x00; /* Set to 0 */ le32 unknown_0x04; /* Size, in bytes, of this 'struct WimOverlay_dat_entry_2', including * wim_file_name and its null terminator. */ le32 entry_2_length; /* Set to 0 */ le32 unknown_0x0C; /* Set to 5 */ le32 unknown_0x10; struct { /* Set to 1 */ le32 unknown_0x14; /* Size of this inner structure, in bytes. */ le32 inner_struct_size; /* Set to 5 */ le32 unknown_0x1C; /* Set to 6 */ le32 unknown_0x20; /* Set to 0 */ le32 unknown_0x24; /* Set to 0x48 */ le32 unknown_0x28; /* Set to 0 */ le32 unknown_0x2C; /************************* * Partition information ************************/ /* Partition identifier */ union { /* (For MBR-formatted disks) */ struct { /* Offset, in bytes, of the MBR partition, from * the beginning of the disk. */ le64 part_start_offset; /* Set to 0 */ le64 padding; } mbr; /* (For GPT-formatted disks) */ struct { /* Unique GUID of the GPT partition */ u8 part_unique_guid[16]; } gpt; } partition; /* Set to 0 */ le32 unknown_0x40; /*********************** * Disk information **********************/ /* 1 for MBR, 0 for GPT */ le32 partition_table_type; #define WIMOVERLAY_PARTITION_TYPE_MBR 1 #define WIMOVERLAY_PARTITION_TYPE_GPT 0 /* Disk identifier */ union { /* (For MBR-formatted disks) */ struct { /* 4-byte ID of the MBR disk */ le32 disk_id; /* Set to 0 */ le32 padding[3]; } mbr; /* (For GPT-formatted disks) */ struct { /* GUID of the GPT disk */ u8 disk_guid[16]; } gpt; } disk; /* Set to 0. (This is the right size for some sort of optional * GUID...) */ le32 unknown_0x58[4]; /************************** * Location in filesystem *************************/ /* Null-terminated path to WIM file. Begins with \ but does * *not* include drive letter! */ utf16lechar wim_file_name[]; } _packed_attribute; } _packed_attribute; static _unused_attribute void wof_check_structs(void) { STATIC_ASSERT(sizeof(struct WimOverlay_dat_header) == 24); STATIC_ASSERT(sizeof(struct WimOverlay_dat_entry_1) == 40); STATIC_ASSERT(sizeof(struct WimOverlay_dat_entry_2) == 104); } /***************************************************************************** * * --- FSCTL_SET_EXTERNAL_BACKING --- * * Sets the backing source of a file. * * DeviceType: 9 (FILE_DEVICE_FILE_SYSTEM) * Access: 0 (FILE_ANY_ACCESS) * Function: 195 * Method: 0 (METHOD_BUFFERED) * * Input buffer: 'struct wof_external_info' followed by provider-specific data * ('struct wim_provider_external_info' in the case of WIM). * * Output buffer: None */ #define FSCTL_SET_EXTERNAL_BACKING 0x9030C struct wim_provider_external_info { /* Set to WIM_PROVIDER_CURRENT_VERSION. */ u32 version; /* 0 when WIM provider active, otherwise * WIM_PROVIDER_EXTERNAL_FLAG_NOT_ACTIVE or * WIM_PROVIDER_EXTERNAL_FLAG_SUSPENDED. */ u32 flags; /* Integer ID that identifies the WIM. Get this with the * FSCTL_ADD_OVERLAY ioctl. */ u64 data_source_id; /* SHA-1 message digest of the file's unnamed data stream. */ u8 unnamed_data_stream_hash[20]; }; struct file_provider_external_info { /* Set to FILE_PROVIDER_CURRENT_VERSION. */ u32 version; u32 compression_format; #define FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS4K 0 #define FILE_PROVIDER_COMPRESSION_FORMAT_LZX 1 #define FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS8K 2 #define FILE_PROVIDER_COMPRESSION_FORMAT_XPRESS16K 3 }; /***************************************************************************** * * --- FSCTL_GET_EXTERNAL_BACKING --- * * Get external backing information for the specified file. * * DeviceType: 9 (FILE_DEVICE_FILE_SYSTEM) * Access: 0 (FILE_ANY_ACCESS) * Function: 196 * Method: 0 (METHOD_BUFFERED) * * Input buffer: None * Output buffer: 'struct wof_external_info' followed by provider-specific data * ('struct wim_provider_external_info' in the case of WIM). */ #define FSCTL_GET_EXTERNAL_BACKING 0x90310 #define STATUS_OBJECT_NOT_EXTERNALLY_BACKED 0xC000046D /***************************************************************************** * * --- FSCTL_DELETE_EXTERNAL_BACKING --- * * Copy a file from its backing source to its volume, then disassociate it from * its backing provider. * * DeviceType: 9 (FILE_DEVICE_FILE_SYSTEM) * Access: 0 (FILE_ANY_ACCESS) * Function: 197 * Method: 0 (METHOD_BUFFERED) * * Input buffer: None * Output buffer: None */ #define FSCTL_DELETE_EXTERNAL_BACKING 0x90314 /***************************************************************************** * * --- FSCTL_ENUM_EXTERNAL_BACKING --- * * Enumerate externally backed files on a volume. * * DeviceType: 9 (FILE_DEVICE_FILE_SYSTEM) * Access: 0 (FILE_ANY_ACCESS) * Function: 198 * Method: 0 (METHOD_BUFFERED) * * Input buffer: None * Output buffer: A 16-byte buffer that receives the 128-bit file ID for the * next externally backed file. * * The handle used may be either the volume handle or the handle for any file or * directory on the volume. * * When all externally backed files on the volume have been enumerated, the * function fails with ERROR_NO_MORE_FILES. */ #define FSCTL_ENUM_EXTERNAL_BACKING 0x90318 /***************************************************************************** * * --- FSCTL_ENUM_OVERLAY --- * * Enumerates the volume's overlay sources from the specified provider. * * DeviceType: 9 (FILE_DEVICE_FILE_SYSTEM) * Access: 0 (FILE_ANY_ACCESS) * Function: 199 * Method: 3 (METHOD_NEITHER) * * Input buffer: 'struct wof_external_info' to specify the provider for which * to enumerate the overlay sources. * * Output buffer: Provider-specific data. For the WIM provider, an array of * 'struct wim_provider_overlay_entry'. * * This ioctl must be performed on the volume handle, such as \\.\C: */ #define FSCTL_ENUM_OVERLAY 0x9031F struct wim_provider_overlay_entry { /* Byte offset of the next entry from the beginning of this structure, * or 0 if there are no more entries. */ u32 next_entry_offset; u32 padding; /* Identifier for the WIM file. */ u64 data_source_id; /* GUID of the WIM file. */ u8 guid[16]; /* Byte offset of the WIM's file name from the beginning of this * structure. */ u32 wim_file_name_offset; /* Type of WIM file: WIM_BOOT_OS_WIM or WIM_BOOT_NOT_OS_WIM. */ u32 wim_type; /* Index of the image in the WIM to use??? (This doesn't really make * sense, since WIM files combine file data "blobs" for all images into * a single table. Set to 1 if unsure...) */ u32 wim_index; /* 0 when WIM provider active, otherwise * WIM_PROVIDER_EXTERNAL_FLAG_NOT_ACTIVE or * WIM_PROVIDER_EXTERNAL_FLAG_SUSPENDED. */ u32 flags; /* Full path to the WIM in the NT device namespace, e.g. * "\Device\HardDiskVolume2\test.wim". Seems to be null-terminated, * although you probably shouldn't assume so. */ wchar_t wim_file_name[]; }; /***************************************************************************** * * --- FSCTL_ADD_OVERLAY --- * * Adds a new external backing source to a volume. * * DeviceType: 9 (FILE_DEVICE_FILE_SYSTEM) * Access: 2 (FILE_WRITE_ACCESS) * Function: 204 * Method: 0 (METHOD_BUFFERED) * * Input buffer: 'struct wof_external_info' followed by provider-specific data * ('struct wim_provider_add_overlay_input' in the case of WIM). * * Output buffer: Buffer large enough to receive any information resulting from * the add operation. For the WIM provider, this must be an 8 byte buffer that * receives the 64-bit WIM file ID. * * This ioctl must be performed on the volume handle, such as \\.\C: */ #define FSCTL_ADD_OVERLAY 0x98330 struct wim_provider_add_overlay_input { /* Type of WIM file. */ u32 wim_type; #define WIM_BOOT_OS_WIM 0 #define WIM_BOOT_NOT_OS_WIM 1 /* Index of the image in the WIM to use??? (This doesn't really make * sense, since WIM files combine file data "blobs" for all images into * a single table. Set to 1 if unsure...) */ u32 wim_index; /* Byte offset of wim_file_name in this buffer, not including the * preceding 'struct wof_external_info' (should be 16). */ u32 wim_file_name_offset; /* Number of bytes in wim_file_name. */ u32 wim_file_name_length; /* Full path to the WIM, e.g. "\??\C:\test-wimboot.wim". * Does NOT need to be null terminated (MS docs claim otherwise). */ wchar_t wim_file_name[]; }; /***************************************************************************** * * --- FSCTL_REMOVE_OVERLAY --- * * Removes an external backing source from a volume. * * DeviceType: 9 (FILE_DEVICE_FILE_SYSTEM) * Access: 2 (FILE_WRITE_ACCESS) * Function: 205 * Method: 0 (METHOD_BUFFERED) * * Input buffer: 'struct wof_external_info' followed by provider-specific data * ('struct wim_provider_remove_overlay_input' in the case of WIM). * * Output buffer: None * * This ioctl must be performed on the volume handle, such as \\.\C: */ #define FSCTL_REMOVE_OVERLAY 0x98334 struct wim_provider_remove_overlay_input { /* Integer ID that identifies the WIM. */ u64 data_source_id; }; /***************************************************************************** * * --- FSCTL_UPDATE_OVERLAY --- * * Updates an overlay source for a volume. * * DeviceType: 9 (FILE_DEVICE_FILE_SYSTEM) * Access: 2 (FILE_WRITE_ACCESS) * Function: 206 * Method: 0 (METHOD_BUFFERED) * * Input buffer: 'struct wof_external_info' followed by provider-specific data * ('struct wim_provider_update_overlay_input' in the case of WIM). * * Output buffer: None * * This ioctl must be performed on the volume handle, such as \\.\C: */ #define FSCTL_UPDATE_OVERLAY 0x98338 struct wim_provider_update_overlay_input { /* Integer ID that identifies the WIM data source. */ u64 data_source_id; /* Byte offset of wim_file_name in this buffer, not including the * preceding 'struct wof_external_info' (should be 16). */ u32 wim_file_name_offset; /* Number of bytes in wim_file_name. */ u32 wim_file_name_length; /* Full path to the WIM, e.g. "\??\C:\test-wimboot.wim". * Does NOT need to be null terminated (MS docs claim otherwise). * This WIM must be renamed from the original WIM, or at least be an * identical copy of it! (Maybe the WIM's GUID field is checked.) */ wchar_t wim_file_name[]; }; #endif /* _WOF_H_ */ wimlib-1.13.1/include/wimlib/x86_cpu_features.h0000644000175000017500000000204413160354224016252 00000000000000#ifndef _WIMLIB_X86_CPU_FEATURES_H #define _WIMLIB_X86_CPU_FEATURES_H #include "wimlib/types.h" #if defined(__i386__) || defined(__x86_64__) #define X86_CPU_FEATURE_SSE 0x00000001 #define X86_CPU_FEATURE_SSE2 0x00000002 #define X86_CPU_FEATURE_SSE3 0x00000004 #define X86_CPU_FEATURE_SSSE3 0x00000008 #define X86_CPU_FEATURE_SSE4_1 0x00000010 #define X86_CPU_FEATURE_SSE4_2 0x00000020 #define X86_CPU_FEATURE_AVX 0x00000040 #define X86_CPU_FEATURE_BMI 0x00000080 #define X86_CPU_FEATURE_AVX2 0x00000100 #define X86_CPU_FEATURE_BMI2 0x00000200 #define X86_CPU_FEATURES_KNOWN 0x80000000 extern u32 _x86_cpu_features; extern void x86_setup_cpu_features(void); /* Does the processor has the specified feature? */ static inline bool x86_have_cpu_feature(u32 feature) { if (!(_x86_cpu_features & X86_CPU_FEATURES_KNOWN)) x86_setup_cpu_features(); return _x86_cpu_features & feature; } #else static inline bool x86_have_cpu_feature(u32 feature) { return false; } #endif /* __i386__ || __x86_64__ */ #endif /* _WIMLIB_X86_CPU_FEATURES_H */ wimlib-1.13.1/include/wimlib/glob.h0000644000175000017500000000142013160354224014000 00000000000000#ifndef _WIMLIB_GLOB_H #define _WIMLIB_GLOB_H #ifndef __WIN32__ # include #else #include #include typedef struct { size_t gl_pathc; wchar_t **gl_pathv; size_t gl_offs; } glob_t; /* WARNING: this is a reduced functionality replacement */ extern int win32_wglob(const wchar_t *pattern, int flags, int (*errfunc)(const wchar_t *epath, int eerrno), glob_t *pglob); extern void globfree(glob_t *pglob); #define GLOB_ERR 0x1 /* Return on read errors. */ #define GLOB_NOSORT 0x2 /* Don't sort the names. */ /* Error returns from `glob'. */ #define GLOB_NOSPACE 1 /* Ran out of memory. */ #define GLOB_ABORTED 2 /* Read error. */ #define GLOB_NOMATCH 3 /* No matches found. */ #endif /* __WIN32__ */ #endif /* _WIMLIB_GLOB_H */ wimlib-1.13.1/include/wimlib/chunk_compressor.h0000644000175000017500000000536713160354224016457 00000000000000/* * chunk_compressor.h * * Interface for serial/parallel chunk compression. */ #ifndef _WIMLIB_CHUNK_COMPRESSOR_H #define _WIMLIB_CHUNK_COMPRESSOR_H #include "wimlib/types.h" /* Interface for chunk compression. Users can submit chunks of data to be * compressed, then retrieve them later in order. This interface can be * implemented either in serial (having the calling thread compress the chunks * itself) or in parallel (having other threads asynchronously compress the * chunks). */ struct chunk_compressor { /* Variables set by the chunk compressor when it is created. */ int out_ctype; u32 out_chunk_size; unsigned num_threads; /* Free the chunk compressor. */ void (*destroy)(struct chunk_compressor *); /* Try to borrow a buffer into which the uncompressed data for the next * chunk should be prepared. * * Only one buffer can be borrowed at a time. * * Returns a pointer to the buffer, or NULL if no buffer is available. * If no buffer is available, you must call ->get_compression_result() * to retrieve a compressed chunk before trying again. */ void *(*get_chunk_buffer)(struct chunk_compressor *); /* Signals to the chunk compressor that the buffer which was loaned out * from ->get_chunk_buffer() has finished being filled and contains the * specified number of bytes of uncompressed data. */ void (*signal_chunk_filled)(struct chunk_compressor *, u32); /* Get the next chunk of compressed data. * * The compressed data, along with its size and the size of the original * uncompressed chunk, are returned in the locations pointed to by * arguments 2-4. The compressed data is in storage internal to the * chunk compressor, and it cannot be accessed beyond any subsequent * calls to the chunk compressor. * * Chunks will be returned in the same order in which they were * submitted for compression. * * The resulting compressed length may be up to the uncompressed length. * In the case where they are equal, the returned data is actually the * uncompressed data, not the compressed data. * * The return value is %true if a chunk of compressed data was * successfully retrieved, or %false if there are no chunks currently * being compressed. */ bool (*get_compression_result)(struct chunk_compressor *, const void **, u32 *, u32 *); }; /* Functions that return implementations of the chunk_compressor interface. */ #ifdef ENABLE_MULTITHREADED_COMPRESSION int new_parallel_chunk_compressor(int out_ctype, u32 out_chunk_size, unsigned num_threads, u64 max_memory, struct chunk_compressor **compressor_ret); #endif int new_serial_chunk_compressor(int out_ctype, u32 out_chunk_size, struct chunk_compressor **compressor_ret); #endif /* _WIMLIB_CHUNK_COMPRESSOR_H */ wimlib-1.13.1/include/wimlib/case.h0000644000175000017500000000136213160354224013775 00000000000000#ifndef _WIMLIB_CASE_H #define _WIMLIB_CASE_H #include /* Note: the NTFS-3G headers define CASE_SENSITIVE, hence the WIMLIB prefix. */ typedef enum { /* Use either case-sensitive or case-insensitive search, depending on * the variable @default_ignore_case. */ WIMLIB_CASE_PLATFORM_DEFAULT = 0, /* Use case-sensitive search. */ WIMLIB_CASE_SENSITIVE = 1, /* Use case-insensitive search. */ WIMLIB_CASE_INSENSITIVE = 2, } CASE_SENSITIVITY_TYPE; extern bool default_ignore_case; static inline bool will_ignore_case(CASE_SENSITIVITY_TYPE case_type) { if (case_type == WIMLIB_CASE_SENSITIVE) return false; if (case_type == WIMLIB_CASE_INSENSITIVE) return true; return default_ignore_case; } #endif /* _WIMLIB_CASE_H */ wimlib-1.13.1/include/wimlib/lzms_constants.h0000644000175000017500000000620413160354224016143 00000000000000/* * lzms_constants.h * * Constants for the LZMS compression format. */ #ifndef _LZMS_CONSTANTS_H #define _LZMS_CONSTANTS_H /* Limits on match lengths and offsets (in bytes) */ #define LZMS_MIN_MATCH_LENGTH 1 #define LZMS_MAX_MATCH_LENGTH 1073809578 #define LZMS_MIN_MATCH_OFFSET 1 #define LZMS_MAX_MATCH_OFFSET 1180427428 /* The value to which buffer sizes should be limited. Microsoft's * implementation seems to use 67108864 (2^26) bytes. However, since the format * itself supports higher match lengths and offsets, we'll use 2^30 bytes. */ #define LZMS_MAX_BUFFER_SIZE 1073741824 /* The length of each LRU queue for match sources: * * 1. offsets of LZ matches * 2. (power, raw offset) pairs of delta matches */ #define LZMS_NUM_LZ_REPS 3 #define LZMS_NUM_DELTA_REPS 3 /* The numbers of binary decision classes needed for encoding queue indices */ #define LZMS_NUM_LZ_REP_DECISIONS (LZMS_NUM_LZ_REPS - 1) #define LZMS_NUM_DELTA_REP_DECISIONS (LZMS_NUM_DELTA_REPS - 1) /* These are the numbers of probability entries for each binary decision class. * The logarithm base 2 of each of these values is the number of recently * encoded bits that are remembered for each decision class and are used to * select the appropriate probability entry when decoding/encoding the next * binary decision (bit). */ #define LZMS_NUM_MAIN_PROBS 16 #define LZMS_NUM_MATCH_PROBS 32 #define LZMS_NUM_LZ_PROBS 64 #define LZMS_NUM_LZ_REP_PROBS 64 #define LZMS_NUM_DELTA_PROBS 64 #define LZMS_NUM_DELTA_REP_PROBS 64 /* These values define the precision for probabilities in LZMS, which are always * given as a numerator; the denominator is implied. */ #define LZMS_PROBABILITY_BITS 6 #define LZMS_PROBABILITY_DENOMINATOR (1 << LZMS_PROBABILITY_BITS) /* These values define the initial state of each probability entry. */ #define LZMS_INITIAL_PROBABILITY 48 #define LZMS_INITIAL_RECENT_BITS 0x0000000055555555 /* The number of symbols in each Huffman-coded alphabet */ #define LZMS_NUM_LITERAL_SYMS 256 #define LZMS_NUM_LENGTH_SYMS 54 #define LZMS_NUM_DELTA_POWER_SYMS 8 #define LZMS_MAX_NUM_OFFSET_SYMS 799 #define LZMS_MAX_NUM_SYMS 799 /* The rebuild frequencies, in symbols, for each Huffman code */ #define LZMS_LITERAL_CODE_REBUILD_FREQ 1024 #define LZMS_LZ_OFFSET_CODE_REBUILD_FREQ 1024 #define LZMS_LENGTH_CODE_REBUILD_FREQ 512 #define LZMS_DELTA_OFFSET_CODE_REBUILD_FREQ 1024 #define LZMS_DELTA_POWER_CODE_REBUILD_FREQ 512 /* The maximum length, in bits, of any Huffman codeword. This is guaranteed by * the way frequencies are updated. */ #define LZMS_MAX_CODEWORD_LENGTH 15 /* The maximum number of verbatim bits, in addition to the Huffman-encoded * length slot symbol, that may be required to encode a match length */ #define LZMS_MAX_EXTRA_LENGTH_BITS 30 /* The maximum number of verbatim bits, in addition to the Huffman-encoded * offset slot symbol, that may be required to encode a match offset */ #define LZMS_MAX_EXTRA_OFFSET_BITS 30 /* Parameters for x86 machine code pre/post-processing */ #define LZMS_X86_ID_WINDOW_SIZE 65535 #define LZMS_X86_MAX_TRANSLATION_OFFSET 1023 #endif /* _LZMS_CONSTANTS_H */ wimlib-1.13.1/include/wimlib/lz_hash.h0000644000175000017500000000253213160354224014512 00000000000000/* * lz_hash.h - hashing for Lempel-Ziv matchfinding * * The following copying information applies to this specific source code file: * * Written in 2014-2015 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . */ #ifndef _LZ_HASH_H #define _LZ_HASH_H #include "wimlib/types.h" /* * The hash function: given a sequence prefix held in the low-order bits of a * 32-bit value, multiply by a carefully-chosen large constant. Discard any * bits of the product that don't fit in a 32-bit value, but take the * next-highest @num_bits bits of the product as the hash value, as those have * the most randomness. */ static forceinline u32 lz_hash(u32 seq, unsigned num_bits) { return (u32)(seq * 0x1E35A7BD) >> (32 - num_bits); } #endif /* _LZ_HASH_H */ wimlib-1.13.1/include/wimlib/util.h0000644000175000017500000000627113324310677014051 00000000000000/* * util.h - utility functions and macros */ #ifndef _WIMLIB_UTIL_H #define _WIMLIB_UTIL_H #include "wimlib/compiler.h" #include "wimlib/types.h" /**************** * General macros *****************/ /* Cast a pointer to a struct member to a pointer to the containing struct. */ #define container_of(ptr, type, member) \ ((type *)((char *)(ptr) - offsetof(type, member))) /* Calculate 'n / d', but round up instead of down. */ #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) /* Calculate 'n % d', but return 'd' if the result would be 0. */ #define MODULO_NONZERO(n, d) (((n) % (d)) ? ((n) % (d)) : (d)) /* Get the number of elements of an array type. */ #define ARRAY_LEN(array) (sizeof(array) / sizeof((array)[0])) /* Round 'v' up to the next 'alignment'-byte aligned boundary. 'alignment' must * be a power of 2. */ #undef ALIGN /* NetBSD defines this already */ #define ALIGN(v, alignment) (((v) + ((alignment) - 1)) & ~((alignment) - 1)) /* Maximum number of bytes that can be allocated on the stack. * * Note: this isn't a hard bound on the stack space used, since this is just for * individual arrays. The full call stack could use more than this. */ #define STACK_MAX 32768 /* Default size of file I/O buffer. Currently assumed to be <= STACK_MAX. */ #define BUFFER_SIZE 32768 /******************* * Memory allocation *******************/ extern void * wimlib_malloc(size_t size) _malloc_attribute; extern void wimlib_free_memory(void *p); extern void * wimlib_realloc(void *ptr, size_t size); extern void * wimlib_calloc(size_t nmemb, size_t size) _malloc_attribute; extern char * wimlib_strdup(const char *str) _malloc_attribute; #ifdef __WIN32__ extern wchar_t * wimlib_wcsdup(const wchar_t *str) _malloc_attribute; #endif extern void * wimlib_aligned_malloc(size_t size, size_t alignment) _malloc_attribute; extern void wimlib_aligned_free(void *ptr); extern void * memdup(const void *mem, size_t size) _malloc_attribute; #define MALLOC wimlib_malloc #define FREE wimlib_free_memory #define REALLOC wimlib_realloc #define CALLOC wimlib_calloc #define STRDUP wimlib_strdup #define WCSDUP wimlib_wcsdup #define ALIGNED_MALLOC wimlib_aligned_malloc #define ALIGNED_FREE wimlib_aligned_free /******************* * String utilities *******************/ #ifndef HAVE_MEMPCPY extern void * mempcpy(void *dst, const void *src, size_t n); #endif /************************** * Random number generation **************************/ extern void get_random_bytes(void *p, size_t n); extern void get_random_alnum_chars(tchar *p, size_t n); /************************ * Hashing and comparison ************************/ static inline bool is_power_of_2(unsigned long n) { return (n != 0 && (n & (n - 1)) == 0); } static inline u64 hash_u64(u64 n) { return n * 0x9e37fffffffc0001ULL; } static inline int cmp_u32(u32 n1, u32 n2) { if (n1 < n2) return -1; if (n1 > n2) return 1; return 0; } static inline int cmp_u64(u64 n1, u64 n2) { if (n1 < n2) return -1; if (n1 > n2) return 1; return 0; } /************************ * System information ************************/ unsigned get_available_cpus(void); u64 get_available_memory(void); #endif /* _WIMLIB_UTIL_H */ wimlib-1.13.1/include/wimlib/alloca.h0000644000175000017500000000024013160354224014307 00000000000000#ifndef _WIMLIB_ALLOCA_H #define _WIMLIB_ALLOCA_H #ifdef HAVE_ALLOCA_H # include #else # include #endif #endif /* _WIMLIB_ALLOCA_H */ wimlib-1.13.1/include/wimlib/encoding.h0000644000175000017500000000655313160354224014657 00000000000000#ifndef _WIMLIB_ENCODING_H #define _WIMLIB_ENCODING_H #include #include "wimlib/error.h" #include "wimlib/util.h" #include "wimlib/types.h" /* String conversion functions */ extern int utf8_to_utf16le(const char *in, size_t in_nbytes, utf16lechar **out_ret, size_t *out_nbytes_ret); extern int utf16le_to_utf8(const utf16lechar *in, size_t in_nbytes, char **out_ret, size_t *out_nbytes_ret); /* Identity conversion: duplicate a 'tchar' string. */ static inline int tstr_to_tstr(const tchar *in, size_t in_nbytes, tchar **out_ret, size_t *out_nbytes_ret) { tchar *out = MALLOC(in_nbytes + sizeof(tchar)); if (unlikely(!out)) return WIMLIB_ERR_NOMEM; memcpy(out, in, in_nbytes); out[in_nbytes / sizeof(tchar)] = 0; *out_ret = out; if (out_nbytes_ret) *out_nbytes_ret = in_nbytes; return 0; } #if TCHAR_IS_UTF16LE /* tstr(UTF-16LE) <=> UTF-16LE */ # define tstr_to_utf16le tstr_to_tstr # define utf16le_to_tstr tstr_to_tstr /* tstr(UTF-16LE) <=> UTF-8 */ # define tstr_to_utf8 utf16le_to_utf8 # define utf8_to_tstr utf8_to_utf16le #else /* tstr(UTF-8) <=> UTF-16LE */ # define tstr_to_utf16le utf8_to_utf16le # define utf16le_to_tstr utf16le_to_utf8 /* tstr(UTF-8) <=> UTF-8 */ # define tstr_to_utf8 tstr_to_tstr # define utf8_to_tstr tstr_to_tstr #endif /* Convert a tchar string to UTF-16LE, but if both encodings are UTF-16LE, then * simply re-use the string. Release with tstr_put_utf16le() when done. */ static inline int tstr_get_utf16le_and_len(const tchar *in, const utf16lechar **out_ret, size_t *out_nbytes_ret) { size_t in_nbytes = tstrlen(in) * sizeof(tchar); #if TCHAR_IS_UTF16LE *out_ret = in; if (out_nbytes_ret) *out_nbytes_ret = in_nbytes; return 0; #else return tstr_to_utf16le(in, in_nbytes, (utf16lechar **)out_ret, out_nbytes_ret); #endif } static inline int tstr_get_utf16le(const tchar *in, const utf16lechar **out_ret) { return tstr_get_utf16le_and_len(in, out_ret, NULL); } /* Release a string acquired with tstr_get_utf16le() or * tstr_get_utf16le_and_len(). */ static inline void tstr_put_utf16le(const utf16lechar *s) { #if !TCHAR_IS_UTF16LE FREE((void *)s); #endif } /* Convert a UTF-16LE string to a tchar string, but if both encodings are * UTF-16LE, then simply re-use the string. Release with utf16le_put_tstr() * when done. */ static inline int utf16le_get_tstr(const utf16lechar *in, size_t in_nbytes, const tchar **out_ret, size_t *out_nbytes_ret) { #if TCHAR_IS_UTF16LE *out_ret = in; if (out_nbytes_ret) *out_nbytes_ret = in_nbytes; return 0; #else return utf16le_to_tstr(in, in_nbytes, (tchar **)out_ret, out_nbytes_ret); #endif } /* Release a string acquired with utf16le_get_tstr(). */ static inline void utf16le_put_tstr(const tchar *s) { #if !TCHAR_IS_UTF16LE FREE((void *)s); #endif } /* UTF-16LE utilities */ extern u16 upcase[65536]; extern void init_upcase(void); extern int cmp_utf16le_strings(const utf16lechar *s1, size_t n1, const utf16lechar *s2, size_t n2, bool ignore_case); extern int cmp_utf16le_strings_z(const utf16lechar *s1, const utf16lechar *s2, bool ignore_case); extern utf16lechar * utf16le_dupz(const void *s, size_t size); extern utf16lechar * utf16le_dup(const utf16lechar *s); extern size_t utf16le_len_bytes(const utf16lechar *s); extern size_t utf16le_len_chars(const utf16lechar *s); #endif /* _WIMLIB_ENCODING_H */ wimlib-1.13.1/include/wimlib/inode.h0000644000175000017500000003420613375671264014201 00000000000000#ifndef _WIMLIB_INODE_H #define _WIMLIB_INODE_H #include "wimlib/assert.h" #include "wimlib/list.h" #include "wimlib/sha1.h" #include "wimlib/types.h" struct avl_tree_node; struct blob_descriptor; struct blob_table; struct wim_dentry; struct wim_inode_extra; struct wim_security_data; struct wimfs_fd; /* Valid values for the 'stream_type' field of a 'struct wim_inode_stream' */ enum wim_inode_stream_type { /* Data stream, may be unnamed (usual case) or named */ STREAM_TYPE_DATA, /* Reparse point stream. This is the same as the data of the on-disk * reparse point attribute, except that the first 8 bytes of the on-disk * attribute are omitted. The omitted bytes contain the reparse tag * (which is instead stored in the on-disk WIM dentry), the reparse data * size (which is redundant with the stream size), and a reserved field * that is always zero. */ STREAM_TYPE_REPARSE_POINT, /* Encrypted data in the "EFSRPC raw data format" specified by [MS-EFSR] * section 2.2.3. This contains metadata for the Encrypting File System * as well as the encrypted data of all the file's data streams. */ STREAM_TYPE_EFSRPC_RAW_DATA, /* Stream type could not be determined */ STREAM_TYPE_UNKNOWN, }; extern const utf16lechar NO_STREAM_NAME[1]; /* * 'struct wim_inode_stream' describes a "stream", which associates a blob of * data with an inode. Each stream has a type and optionally a name. * * The most frequently seen kind of stream is the "unnamed data stream" * (stream_type == STREAM_TYPE_DATA && stream_name == NO_STREAM_NAME), which is * the "default file contents". Many inodes just have an unnamed data stream * and no other streams. However, files on NTFS filesystems may have * additional, "named" data streams, and this is supported by the WIM format. * * A "reparse point" is an inode with reparse data set. The reparse data is * stored in a stream of type STREAM_TYPE_REPARSE_POINT. There should be only * one such stream, and it should be unnamed. However, it is possible for an * inode to have both a reparse point stream and an unnamed data stream, and * even named data streams as well. */ struct wim_inode_stream { /* The name of the stream or NO_STREAM_NAME. */ utf16lechar *stream_name; /* * If 'stream_resolved' = 0, then 'stream_hash' is the SHA-1 message * digest of the uncompressed data of this stream, or all zeroes if this * stream is empty. * * If 'stream_resolved' = 1, then 'stream_blob' is a pointer directly to * a descriptor for this stream's blob, or NULL if this stream is empty. */ union { u8 _stream_hash[SHA1_HASH_SIZE]; struct blob_descriptor *_stream_blob; } _packed_attribute; /* union is SHA1_HASH_SIZE bytes */ /* 'stream_resolved' determines whether 'stream_hash' or 'stream_blob' * is valid as described above. */ u32 stream_resolved : 1; /* A unique identifier for this stream within the context of its inode. * This stays constant even if the streams array is reallocated. */ u32 stream_id : 28; /* The type of this stream as one of the STREAM_TYPE_* values */ u32 stream_type : 3; }; /* * WIM inode - a "file" in an image which may be accessible via multiple paths * * Note: in WIM files there is no true on-disk analogue of an inode; there are * only directory entries, and some fields are duplicated among all links to a * file. However, wimlib uses inode structures internally to simplify handling * of hard links. */ struct wim_inode { /* * The collection of streams for this inode. 'i_streams' points to * either 'i_embedded_streams' or an allocated array. */ struct wim_inode_stream *i_streams; struct wim_inode_stream i_embedded_streams[1]; unsigned i_num_streams; /* Windows file attribute flags (FILE_ATTRIBUTE_*). */ u32 i_attributes; /* Root of a balanced binary search tree storing the child directory * entries of this inode, if any, indexed by filename. If this inode is * not a directory or if it has no children then this will be an empty * tree (NULL). */ struct avl_tree_node *i_children; /* List of dentries that are aliases for this inode. There will be * i_nlink dentries in this list. */ struct hlist_head i_alias_list; /* Field to place this inode into a list. While reading a WIM image or * adding files to a WIM image this is owned by the inode table; * otherwise this links the inodes for the WIM image. */ struct hlist_node i_hlist_node; /* Number of dentries that are aliases for this inode. */ u32 i_nlink : 30; /* Flag used by some code to mark this inode as visited. It will be 0 * by default, and it always must be cleared after use. */ u32 i_visited : 1; /* Cached value */ u32 i_can_externally_back : 1; /* If not NULL, a pointer to the extra data that was read from the * dentry. This should be a series of tagged items, each of which * represents a bit of extra metadata, such as the file's object ID. * See tagged_items.c for more information. */ struct wim_inode_extra *i_extra; /* Creation time, last access time, and last write time for this inode, * in 100-nanosecond intervals since 12:00 a.m UTC January 1, 1601. * They should correspond to the times gotten by calling GetFileTime() * on Windows. */ u64 i_creation_time; u64 i_last_access_time; u64 i_last_write_time; /* Corresponds to 'security_id' in `struct wim_dentry_on_disk': The * index of this inode's security descriptor in the WIM image's table of * security descriptors, or -1 if this inode does not have a security * descriptor. */ s32 i_security_id; /* Unknown field that we only read into memory so we can re-write it * unchanged. Probably it's actually just padding... */ u32 i_unknown_0x54; /* The following fields correspond to 'reparse_tag', 'rp_reserved', and * 'rp_flags' in `struct wim_dentry_on_disk'. They are only meaningful * for reparse point files. */ u32 i_reparse_tag; u16 i_rp_reserved; u16 i_rp_flags; /* Inode number; corresponds to hard_link_group_id in the `struct * wim_dentry_on_disk'. */ u64 i_ino; union { /* Device number, used only during image capture, so we can * identify hard linked files by the combination of inode number * and device number (rather than just inode number, which could * be ambiguous if the captured tree spans a mountpoint). Set * to 0 otherwise. */ u64 i_devno; /* Fields used only during extraction */ struct { /* A singly linked list of aliases of this inode that * are being extracted in the current extraction * operation. This list may be shorter than the inode's * full alias list. This list will be constructed * regardless of whether the extraction backend supports * hard links or not. */ struct wim_dentry *i_first_extraction_alias; #ifdef WITH_NTFS_3G /* In NTFS-3G extraction mode, this is set to the Master * File Table (MFT) number of the NTFS file that was * created for this inode. */ u64 i_mft_no; #endif }; /* Used during WIM writing with * WIMLIB_WRITE_FLAG_SEND_DONE_WITH_FILE_MESSAGES: the number * of streams this inode has that have not yet been fully read. * */ u32 i_num_remaining_streams; #ifdef WITH_FUSE struct { /* Used only during image mount: Table of file * descriptors that have been opened to this inode. * This table is freed when the last file descriptor is * closed. */ struct wimfs_fd **i_fds; /* Lower bound on the index of the next available entry * in 'i_fds'. */ u16 i_next_fd; }; #endif }; #ifdef WITH_FUSE u16 i_num_opened_fds; u16 i_num_allocated_fds; #endif /* Next stream ID to be assigned */ u32 i_next_stream_id; #ifdef ENABLE_TEST_SUPPORT struct wim_inode *i_corresponding; #endif }; /* Optional extra data for a WIM inode */ struct wim_inode_extra { size_t size; /* Size of the extra data in bytes */ u8 data[] _aligned_attribute(8); /* The extra data */ }; /* * The available reparse tags are documented at * http://msdn.microsoft.com/en-us/library/dd541667(v=prot.10).aspx. * Here we only define the ones of interest to us. */ #define WIM_IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 #define WIM_IO_REPARSE_TAG_SYMLINK 0xA000000C #define WIM_IO_REPARSE_TAG_DEDUP 0x80000013 #define WIM_IO_REPARSE_TAG_WOF 0x80000017 /* Flags for the rp_flags field. Currently the only known flag is NOT_FIXED, * which indicates that the target of the absolute symbolic link or junction was * not changed when it was stored. */ #define WIM_RP_FLAG_NOT_FIXED 0x0001 /* Windows file attribute flags */ #define FILE_ATTRIBUTE_READONLY 0x00000001 #define FILE_ATTRIBUTE_HIDDEN 0x00000002 #define FILE_ATTRIBUTE_SYSTEM 0x00000004 #define FILE_ATTRIBUTE_DIRECTORY 0x00000010 #define FILE_ATTRIBUTE_ARCHIVE 0x00000020 #define FILE_ATTRIBUTE_DEVICE 0x00000040 #define FILE_ATTRIBUTE_NORMAL 0x00000080 #define FILE_ATTRIBUTE_TEMPORARY 0x00000100 #define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200 #define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400 #define FILE_ATTRIBUTE_COMPRESSED 0x00000800 #define FILE_ATTRIBUTE_OFFLINE 0x00001000 #define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000 #define FILE_ATTRIBUTE_ENCRYPTED 0x00004000 #define FILE_ATTRIBUTE_VIRTUAL 0x00010000 extern struct wim_inode * new_inode(struct wim_dentry *dentry, bool set_timestamps); /* Iterate through each alias of the specified inode. */ #define inode_for_each_dentry(dentry, inode) \ hlist_for_each_entry((dentry), &(inode)->i_alias_list, d_alias_node) /* Return an alias of the specified inode. */ #define inode_any_dentry(inode) \ hlist_entry(inode->i_alias_list.first, struct wim_dentry, d_alias_node) /* Return the full path of an alias of the specified inode, or NULL if a full * path could not be determined. */ #define inode_any_full_path(inode) \ dentry_full_path(inode_any_dentry(inode)) extern void d_associate(struct wim_dentry *dentry, struct wim_inode *inode); extern void d_disassociate(struct wim_dentry *dentry); #ifdef WITH_FUSE extern void inode_dec_num_opened_fds(struct wim_inode *inode); #endif /* Is the inode a directory? * This doesn't count directories with reparse data. * wimlib only allows inodes of this type to have children. */ static inline bool inode_is_directory(const struct wim_inode *inode) { return (inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)) == FILE_ATTRIBUTE_DIRECTORY; } /* Is the inode a symbolic link? * This returns true iff the inode is a reparse point that is either a "real" * symbolic link or a junction point. */ static inline bool inode_is_symlink(const struct wim_inode *inode) { return (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) && (inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK || inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT); } /* Does the inode have children? Currently (based on read_dentry_tree() as well * as the various build-dentry-tree implementations), this can only return true * for inodes for which inode_is_directory() returns true. */ static inline bool inode_has_children(const struct wim_inode *inode) { return inode->i_children != NULL; } /* Does the inode have a security descriptor? */ static inline bool inode_has_security_descriptor(const struct wim_inode *inode) { return inode->i_security_id >= 0; } extern struct wim_inode_stream * inode_get_stream(const struct wim_inode *inode, int stream_type, const utf16lechar *stream_name); extern struct wim_inode_stream * inode_get_unnamed_stream(const struct wim_inode *inode, int stream_type); static inline struct wim_inode_stream * inode_get_unnamed_data_stream(const struct wim_inode *inode) { return inode_get_unnamed_stream(inode, STREAM_TYPE_DATA); } extern struct wim_inode_stream * inode_add_stream(struct wim_inode *inode, int stream_type, const utf16lechar *stream_name, struct blob_descriptor *blob); extern void inode_replace_stream_blob(struct wim_inode *inode, struct wim_inode_stream *strm, struct blob_descriptor *new_blob, struct blob_table *blob_table); extern bool inode_replace_stream_data(struct wim_inode *inode, struct wim_inode_stream *strm, const void *data, size_t size, struct blob_table *blob_table); extern bool inode_add_stream_with_data(struct wim_inode *inode, int stream_type, const utf16lechar *stream_name, const void *data, size_t size, struct blob_table *blob_table); extern void inode_remove_stream(struct wim_inode *inode, struct wim_inode_stream *strm, struct blob_table *blob_table); static inline struct blob_descriptor * stream_blob_resolved(const struct wim_inode_stream *strm) { wimlib_assert(strm->stream_resolved); return strm->_stream_blob; } static inline bool stream_is_named(const struct wim_inode_stream *strm) { return strm->stream_name != NO_STREAM_NAME; } static inline bool stream_is_unnamed_data_stream(const struct wim_inode_stream *strm) { return strm->stream_type == STREAM_TYPE_DATA && !stream_is_named(strm); } static inline bool stream_is_named_data_stream(const struct wim_inode_stream *strm) { return strm->stream_type == STREAM_TYPE_DATA && stream_is_named(strm); } extern bool inode_has_named_data_stream(const struct wim_inode *inode); extern int inode_resolve_streams(struct wim_inode *inode, struct blob_table *table, bool force); extern int blob_not_found_error(const struct wim_inode *inode, const u8 *hash); extern struct blob_descriptor * stream_blob(const struct wim_inode_stream *strm, const struct blob_table *table); extern struct blob_descriptor * inode_get_blob_for_unnamed_data_stream(const struct wim_inode *inode, const struct blob_table *blob_table); extern struct blob_descriptor * inode_get_blob_for_unnamed_data_stream_resolved(const struct wim_inode *inode); extern const u8 * stream_hash(const struct wim_inode_stream *strm); extern const u8 * inode_get_hash_of_unnamed_data_stream(const struct wim_inode *inode); extern void inode_ref_blobs(struct wim_inode *inode); extern void inode_unref_blobs(struct wim_inode *inode, struct blob_table *blob_table); /* inode_fixup.c */ extern int dentry_tree_fix_inodes(struct wim_dentry *root, struct hlist_head *inode_list); #endif /* _WIMLIB_INODE_H */ wimlib-1.13.1/include/wimlib/tagged_items.h0000644000175000017500000000136413272231267015525 00000000000000#ifndef _WIMLIB_TAGGED_ITEMS_H #define _WIMLIB_TAGGED_ITEMS_H #include "wimlib/types.h" struct wim_inode; /* Windows-style object ID */ #define TAG_OBJECT_ID 0x00000001 /* Extended attributes */ #define TAG_XATTRS 0x00000002 /* [wimlib extension] Standard UNIX metadata: uid, gid, mode, and rdev */ #define TAG_WIMLIB_UNIX_DATA 0x337DD873 /* * [wimlib extension] Linux-style extended attributes * (deprecated in favor of TAG_XATTRS) */ #define TAG_WIMLIB_LINUX_XATTRS 0x337DD874 extern void * inode_get_tagged_item(const struct wim_inode *inode, u32 tag, u32 min_len, u32 *actual_len_ret); extern bool inode_set_tagged_item(struct wim_inode *inode, u32 tag, const void *data, u32 len); #endif /* _WIMLIB_TAGGED_ITEMS_H */ wimlib-1.13.1/include/wimlib/xml.h0000644000175000017500000000420413160354224013660 00000000000000#ifndef _WIMLIB_XML_H #define _WIMLIB_XML_H #include "wimlib/types.h" /*****************************************************************************/ struct wim_xml_info; extern struct wim_xml_info * xml_new_info_struct(void); extern void xml_free_info_struct(struct wim_xml_info *info); /*****************************************************************************/ extern int xml_get_image_count(const struct wim_xml_info *info); extern u64 xml_get_total_bytes(const struct wim_xml_info *info); extern u64 xml_get_image_total_bytes(const struct wim_xml_info *info, int image); extern u64 xml_get_image_hard_link_bytes(const struct wim_xml_info *info, int image); extern bool xml_get_wimboot(const struct wim_xml_info *info, int image); extern u64 xml_get_windows_build_number(const struct wim_xml_info *info, int image); extern int xml_set_wimboot(struct wim_xml_info *info, int image); /*****************************************************************************/ extern int xml_update_image_info(WIMStruct *wim, int image); extern int xml_add_image(struct wim_xml_info *info, const tchar *name); extern int xml_export_image(const struct wim_xml_info *src_info, int src_image, struct wim_xml_info *dest_info, const tchar *dest_image_name, const tchar *dest_image_description, bool wimboot); extern void xml_delete_image(struct wim_xml_info *info, int image); extern void xml_print_image_info(struct wim_xml_info *info, int image); /*****************************************************************************/ struct wim_reshdr; #define WIM_TOTALBYTES_USE_EXISTING ((u64)(-1)) #define WIM_TOTALBYTES_OMIT ((u64)(-2)) extern int read_wim_xml_data(WIMStruct *wim); extern int write_wim_xml_data(WIMStruct *wim, int image, u64 total_bytes, struct wim_reshdr *out_reshdr, int write_resource_flags); /*****************************************************************************/ extern void xml_global_init(void); extern void xml_global_cleanup(void); extern void xml_set_memory_allocator(void *(*malloc_func)(size_t), void (*free_func)(void *), void *(*realloc_func)(void *, size_t)); #endif /* _WIMLIB_XML_H */ wimlib-1.13.1/include/wimlib/win32.h0000644000175000017500000000264713160354224014033 00000000000000/* * win32.h - Windows-specific declarations needed by non-Windows-specific files. */ #ifndef _WIMLIB_WIN32_H #define _WIMLIB_WIN32_H #ifdef __WIN32__ #include "wimlib/types.h" struct blob_descriptor; struct consume_chunk_callback; struct windows_file; extern struct windows_file * clone_windows_file(const struct windows_file *file); extern void free_windows_file(struct windows_file *file); extern int cmp_windows_files(const struct windows_file *file1, const struct windows_file *file2); extern int read_windows_file_prefix(const struct blob_descriptor *blob, u64 size, const struct consume_chunk_callback *cb); extern int win32_global_init(int init_flags); extern void win32_global_cleanup(void); extern int fsync(int fd); extern tchar * realpath(const tchar *path, tchar *resolved_path); extern int win32_rename_replacement(const tchar *oldpath, const tchar *newpath); extern int win32_truncate_replacement(const tchar *path, off_t size); extern int win32_strerror_r_replacement(int errnum, tchar *buf, size_t buflen); extern FILE * win32_open_logfile(const wchar_t *path); extern ssize_t win32_read(int fd, void *buf, size_t count); extern ssize_t win32_write(int fd, const void *buf, size_t count); extern ssize_t win32_pread(int fd, void *buf, size_t count, off_t offset); extern ssize_t win32_pwrite(int fd, const void *buf, size_t count, off_t offset); #endif /* __WIN32__ */ #endif /* _WIMLIB_WIN32_H */ wimlib-1.13.1/include/wimlib/lzx_common.h0000644000175000017500000000107713160354224015252 00000000000000/* * lzx_common.h * * Declarations shared between LZX compression and decompression. */ #ifndef _LZX_COMMON_H #define _LZX_COMMON_H #include "wimlib/lzx_constants.h" #include "wimlib/types.h" extern const s32 lzx_offset_slot_base[LZX_MAX_OFFSET_SLOTS + 1]; extern const u8 lzx_extra_offset_bits[LZX_MAX_OFFSET_SLOTS]; extern unsigned lzx_get_window_order(size_t max_bufsize); extern unsigned lzx_get_num_main_syms(unsigned window_order); extern void lzx_preprocess(u8 *data, u32 size); extern void lzx_postprocess(u8 *data, u32 size); #endif /* _LZX_COMMON_H */ wimlib-1.13.1/include/wimlib/bt_matchfinder.h0000644000175000017500000003026613160354224016040 00000000000000/* * bt_matchfinder.h - Lempel-Ziv matchfinding with a hash table of binary trees * * The following copying information applies to this specific source code file: * * Written in 2014-2016 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . * * ---------------------------------------------------------------------------- * * This is a Binary Trees (bt) based matchfinder. * * The main data structure is a hash table where each hash bucket contains a * binary tree of sequences whose first 4 bytes share the same hash code. Each * sequence is identified by its starting position in the input buffer. Each * binary tree is always sorted such that each left child represents a sequence * lexicographically lesser than its parent and each right child represents a * sequence lexicographically greater than its parent. * * The algorithm processes the input buffer sequentially. At each byte * position, the hash code of the first 4 bytes of the sequence beginning at * that position (the sequence being matched against) is computed. This * identifies the hash bucket to use for that position. Then, a new binary tree * node is created to represent the current sequence. Then, in a single tree * traversal, the hash bucket's binary tree is searched for matches and is * re-rooted at the new node. * * Compared to the simpler algorithm that uses linked lists instead of binary * trees (see hc_matchfinder.h), the binary tree version gains more information * at each node visitation. Ideally, the binary tree version will examine only * 'log(n)' nodes to find the same matches that the linked list version will * find by examining 'n' nodes. In addition, the binary tree version can * examine fewer bytes at each node by taking advantage of the common prefixes * that result from the sort order, whereas the linked list version may have to * examine up to the full length of the match at each node. * * However, it is not always best to use the binary tree version. It requires * nearly twice as much memory as the linked list version, and it takes time to * keep the binary trees sorted, even at positions where the compressor does not * need matches. Generally, when doing fast compression on small buffers, * binary trees are the wrong approach. They are best suited for thorough * compression and/or large buffers. * * ---------------------------------------------------------------------------- */ #include #include "wimlib/lz_extend.h" #include "wimlib/lz_hash.h" #define BT_MATCHFINDER_HASH3_ORDER 15 #define BT_MATCHFINDER_HASH3_WAYS 2 #define BT_MATCHFINDER_HASH4_ORDER 16 /* TEMPLATED functions and structures have MF_SUFFIX appended to their name. */ #undef TEMPLATED #define TEMPLATED(name) CONCAT(name, MF_SUFFIX) #ifndef _WIMLIB_BT_MATCHFINDER_H #define _WIMLIB_BT_MATCHFINDER_H /* Non-templated definitions */ /* Representation of a match found by the bt_matchfinder */ struct lz_match { /* The number of bytes matched. */ u32 length; /* The offset back from the current position that was matched. */ u32 offset; }; #endif /* _WIMLIB_BT_MATCHFINDER_H */ struct TEMPLATED(bt_matchfinder) { /* The hash table for finding length 2 matches, if enabled */ #ifdef BT_MATCHFINDER_HASH2_ORDER mf_pos_t hash2_tab[1UL << BT_MATCHFINDER_HASH2_ORDER]; #endif /* The hash table for finding length 3 matches */ mf_pos_t hash3_tab[1UL << BT_MATCHFINDER_HASH3_ORDER][BT_MATCHFINDER_HASH3_WAYS]; /* The hash table which contains the roots of the binary trees for * finding length 4+ matches */ mf_pos_t hash4_tab[1UL << BT_MATCHFINDER_HASH4_ORDER]; /* The child node references for the binary trees. The left and right * children of the node for the sequence with position 'pos' are * 'child_tab[pos * 2]' and 'child_tab[pos * 2 + 1]', respectively. */ mf_pos_t child_tab[]; }; /* Return the number of bytes that must be allocated for a 'bt_matchfinder' that * can work with buffers up to the specified size. */ static forceinline size_t TEMPLATED(bt_matchfinder_size)(size_t max_bufsize) { return sizeof(struct TEMPLATED(bt_matchfinder)) + (2 * max_bufsize * sizeof(mf_pos_t)); } /* Prepare the matchfinder for a new input buffer. */ static forceinline void TEMPLATED(bt_matchfinder_init)(struct TEMPLATED(bt_matchfinder) *mf) { memset(mf, 0, sizeof(*mf)); } static forceinline mf_pos_t * TEMPLATED(bt_left_child)(struct TEMPLATED(bt_matchfinder) *mf, u32 node) { return &mf->child_tab[(node << 1) + 0]; } static forceinline mf_pos_t * TEMPLATED(bt_right_child)(struct TEMPLATED(bt_matchfinder) *mf, u32 node) { return &mf->child_tab[(node << 1) + 1]; } /* The minimum permissible value of 'max_len' for bt_matchfinder_get_matches() * and bt_matchfinder_skip_position(). There must be sufficiently many bytes * remaining to load a 32-bit integer from the *next* position. */ #define BT_MATCHFINDER_REQUIRED_NBYTES 5 /* Advance the binary tree matchfinder by one byte, optionally recording * matches. @record_matches should be a compile-time constant. */ static forceinline struct lz_match * TEMPLATED(bt_matchfinder_advance_one_byte)(struct TEMPLATED(bt_matchfinder) * const restrict mf, const u8 * const restrict in_begin, const ptrdiff_t cur_pos, const u32 max_len, const u32 nice_len, const u32 max_search_depth, u32 next_hashes[const restrict static 2], u32 * const restrict best_len_ret, struct lz_match * restrict lz_matchptr, const bool record_matches) { const u8 *in_next = in_begin + cur_pos; u32 depth_remaining = max_search_depth; u32 next_seq4; u32 next_seq3; u32 hash3; u32 hash4; #ifdef BT_MATCHFINDER_HASH2_ORDER u16 seq2; u32 hash2; #endif STATIC_ASSERT(BT_MATCHFINDER_HASH3_WAYS >= 1 && BT_MATCHFINDER_HASH3_WAYS <= 2); u32 cur_node; #if BT_MATCHFINDER_HASH3_WAYS >= 2 u32 cur_node_2; #endif const u8 *matchptr; mf_pos_t *pending_lt_ptr, *pending_gt_ptr; u32 best_lt_len, best_gt_len; u32 len; u32 best_len = 3; next_seq4 = load_u32_unaligned(in_next + 1); next_seq3 = loaded_u32_to_u24(next_seq4); hash3 = next_hashes[0]; hash4 = next_hashes[1]; next_hashes[0] = lz_hash(next_seq3, BT_MATCHFINDER_HASH3_ORDER); next_hashes[1] = lz_hash(next_seq4, BT_MATCHFINDER_HASH4_ORDER); prefetchw(&mf->hash3_tab[next_hashes[0]]); prefetchw(&mf->hash4_tab[next_hashes[1]]); #ifdef BT_MATCHFINDER_HASH2_ORDER seq2 = load_u16_unaligned(in_next); hash2 = lz_hash(seq2, BT_MATCHFINDER_HASH2_ORDER); cur_node = mf->hash2_tab[hash2]; mf->hash2_tab[hash2] = cur_pos; if (record_matches && seq2 == load_u16_unaligned(&in_begin[cur_node]) && likely(in_next != in_begin)) { lz_matchptr->length = 2; lz_matchptr->offset = in_next - &in_begin[cur_node]; lz_matchptr++; } #endif cur_node = mf->hash3_tab[hash3][0]; mf->hash3_tab[hash3][0] = cur_pos; #if BT_MATCHFINDER_HASH3_WAYS >= 2 cur_node_2 = mf->hash3_tab[hash3][1]; mf->hash3_tab[hash3][1] = cur_node; #endif if (record_matches && likely(in_next != in_begin)) { u32 seq3 = load_u24_unaligned(in_next); if (seq3 == load_u24_unaligned(&in_begin[cur_node])) { lz_matchptr->length = 3; lz_matchptr->offset = in_next - &in_begin[cur_node]; lz_matchptr++; } #if BT_MATCHFINDER_HASH3_WAYS >= 2 else if (seq3 == load_u24_unaligned(&in_begin[cur_node_2])) { lz_matchptr->length = 3; lz_matchptr->offset = in_next - &in_begin[cur_node_2]; lz_matchptr++; } #endif } cur_node = mf->hash4_tab[hash4]; mf->hash4_tab[hash4] = cur_pos; pending_lt_ptr = TEMPLATED(bt_left_child)(mf, cur_pos); pending_gt_ptr = TEMPLATED(bt_right_child)(mf, cur_pos); if (!cur_node) { *pending_lt_ptr = 0; *pending_gt_ptr = 0; *best_len_ret = best_len; return lz_matchptr; } best_lt_len = 0; best_gt_len = 0; len = 0; for (;;) { matchptr = &in_begin[cur_node]; if (matchptr[len] == in_next[len]) { len = lz_extend(in_next, matchptr, len + 1, max_len); if (!record_matches || len > best_len) { if (record_matches) { best_len = len; lz_matchptr->length = len; lz_matchptr->offset = in_next - matchptr; lz_matchptr++; } if (len >= nice_len) { *pending_lt_ptr = *TEMPLATED(bt_left_child)(mf, cur_node); *pending_gt_ptr = *TEMPLATED(bt_right_child)(mf, cur_node); *best_len_ret = best_len; return lz_matchptr; } } } if (matchptr[len] < in_next[len]) { *pending_lt_ptr = cur_node; pending_lt_ptr = TEMPLATED(bt_right_child)(mf, cur_node); cur_node = *pending_lt_ptr; best_lt_len = len; if (best_gt_len < len) len = best_gt_len; } else { *pending_gt_ptr = cur_node; pending_gt_ptr = TEMPLATED(bt_left_child)(mf, cur_node); cur_node = *pending_gt_ptr; best_gt_len = len; if (best_lt_len < len) len = best_lt_len; } if (!cur_node || !--depth_remaining) { *pending_lt_ptr = 0; *pending_gt_ptr = 0; *best_len_ret = best_len; return lz_matchptr; } } } /* * Retrieve a list of matches with the current position. * * @mf * The matchfinder structure. * @in_begin * Pointer to the beginning of the input buffer. * @cur_pos * The current position in the input buffer (the position of the sequence * being matched against). * @max_len * The maximum permissible match length at this position. Must be >= * BT_MATCHFINDER_REQUIRED_NBYTES. * @nice_len * Stop searching if a match of at least this length is found. * Must be <= @max_len. * @max_search_depth * Limit on the number of potential matches to consider. Must be >= 1. * @next_hashes * The precomputed hash codes for the sequence beginning at @in_next. * These will be used and then updated with the precomputed hashcodes for * the sequence beginning at @in_next + 1. * @best_len_ret * If a match of length >= 4 was found, then the length of the longest such * match is written here; otherwise 3 is written here. (Note: this is * redundant with the 'struct lz_match' array, but this is easier for the * compiler to optimize when inlined and the caller immediately does a * check against 'best_len'.) * @lz_matchptr * An array in which this function will record the matches. The recorded * matches will be sorted by strictly increasing length and (non-strictly) * increasing offset. The maximum number of matches that may be found is * 'nice_len - 1', or one less if length 2 matches are disabled. * * The return value is a pointer to the next available slot in the @lz_matchptr * array. (If no matches were found, this will be the same as @lz_matchptr.) */ static forceinline struct lz_match * TEMPLATED(bt_matchfinder_get_matches)(struct TEMPLATED(bt_matchfinder) *mf, const u8 *in_begin, ptrdiff_t cur_pos, u32 max_len, u32 nice_len, u32 max_search_depth, u32 next_hashes[static 2], u32 *best_len_ret, struct lz_match *lz_matchptr) { return TEMPLATED(bt_matchfinder_advance_one_byte)(mf, in_begin, cur_pos, max_len, nice_len, max_search_depth, next_hashes, best_len_ret, lz_matchptr, true); } /* * Advance the matchfinder, but don't record any matches. * * This is very similar to bt_matchfinder_get_matches() because both functions * must do hashing and tree re-rooting. */ static forceinline void TEMPLATED(bt_matchfinder_skip_position)(struct TEMPLATED(bt_matchfinder) *mf, const u8 *in_begin, ptrdiff_t cur_pos, u32 nice_len, u32 max_search_depth, u32 next_hashes[static 2]) { u32 best_len; TEMPLATED(bt_matchfinder_advance_one_byte)(mf, in_begin, cur_pos, nice_len, nice_len, max_search_depth, next_hashes, &best_len, NULL, false); } wimlib-1.13.1/include/wimlib/test_support.h0000644000175000017500000000104513160354224015633 00000000000000#ifndef _WIMLIB_TEST_SUPPORT_H #define _WIMLIB_TEST_SUPPORT_H #ifdef ENABLE_TEST_SUPPORT #include "wimlib/types.h" #define WIMLIB_ERR_IMAGES_ARE_DIFFERENT 200 #define WIMLIB_ADD_FLAG_GENERATE_TEST_DATA 0x08000000 #define WIMLIB_CMP_FLAG_UNIX_MODE 0x00000001 #define WIMLIB_CMP_FLAG_NTFS_3G_MODE 0x00000002 #define WIMLIB_CMP_FLAG_WINDOWS_MODE 0x00000004 extern int wimlib_compare_images(WIMStruct *wim1, int image1, WIMStruct *wim2, int image2, int cmp_flags); #endif /* ENABLE_TEST_SUPPORT */ #endif /* _WIMLIB_TEST_SUPPORT_H */ wimlib-1.13.1/include/wimlib/lz_extend.h0000644000175000017500000000320713160354224015056 00000000000000/* * lz_extend.h - fast match extension for Lempel-Ziv matchfinding * * The following copying information applies to this specific source code file: * * Written in 2014-2016 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . */ #ifndef _WIMLIB_LZ_EXTEND_H #define _WIMLIB_LZ_EXTEND_H #include "wimlib/bitops.h" #include "wimlib/unaligned.h" /* * Return the number of bytes at @matchptr that match the bytes at @strptr, up * to a maximum of @max_len. Initially, @len bytes are matched. */ static forceinline u32 lz_extend(const u8 * const strptr, const u8 * const matchptr, u32 len, const u32 max_len) { while (UNALIGNED_ACCESS_IS_FAST && len + WORDBYTES <= max_len) { machine_word_t v = load_word_unaligned(matchptr + len) ^ load_word_unaligned(strptr + len); if (v != 0) { if (CPU_IS_LITTLE_ENDIAN) len += bsfw(v) >> 3; else len += (WORDBITS - 1 - bsrw(v)) >> 3; return len; } len += WORDBYTES; } while (len < max_len && matchptr[len] == strptr[len]) len++; return len; } #endif /* _WIMLIB_LZ_EXTEND_H */ wimlib-1.13.1/include/wimlib/lzx_constants.h0000644000175000017500000000767713160354224016012 00000000000000/* * lzx_constants.h * * Constants for the LZX compression format. */ #ifndef _LZX_CONSTANTS_H #define _LZX_CONSTANTS_H /* Number of literal byte values. */ #define LZX_NUM_CHARS 256 /* The smallest and largest allowed match lengths. */ #define LZX_MIN_MATCH_LEN 2 #define LZX_MAX_MATCH_LEN 257 /* Number of distinct match lengths that can be represented. */ #define LZX_NUM_LENS (LZX_MAX_MATCH_LEN - LZX_MIN_MATCH_LEN + 1) /* Number of match lengths for which no length symbol is required. */ #define LZX_NUM_PRIMARY_LENS 7 #define LZX_NUM_LEN_HEADERS (LZX_NUM_PRIMARY_LENS + 1) /* The first length which requires a length symbol. */ #define LZX_MIN_SECONDARY_LEN (LZX_MIN_MATCH_LEN + LZX_NUM_PRIMARY_LENS) /* Valid values of the 3-bit block type field. */ #define LZX_BLOCKTYPE_VERBATIM 1 #define LZX_BLOCKTYPE_ALIGNED 2 #define LZX_BLOCKTYPE_UNCOMPRESSED 3 /* 'LZX_MIN_WINDOW_SIZE' and 'LZX_MAX_WINDOW_SIZE' are the minimum and maximum * sizes of the sliding window. */ #define LZX_MIN_WINDOW_ORDER 15 #define LZX_MAX_WINDOW_ORDER 21 #define LZX_MIN_WINDOW_SIZE (1UL << LZX_MIN_WINDOW_ORDER) /* 32768 */ #define LZX_MAX_WINDOW_SIZE (1UL << LZX_MAX_WINDOW_ORDER) /* 2097152 */ /* Maximum number of offset slots. (The actual number of offset slots depends * on the window size.) */ #define LZX_MAX_OFFSET_SLOTS 50 /* Maximum number of symbols in the main code. (The actual number of symbols in * the main code depends on the window size.) */ #define LZX_MAINCODE_MAX_NUM_SYMBOLS \ (LZX_NUM_CHARS + (LZX_MAX_OFFSET_SLOTS * LZX_NUM_LEN_HEADERS)) /* Number of symbols in the length code. */ #define LZX_LENCODE_NUM_SYMBOLS (LZX_NUM_LENS - LZX_NUM_PRIMARY_LENS) /* Number of symbols in the pre-code. */ #define LZX_PRECODE_NUM_SYMBOLS 20 /* Number of bits in which each pre-code codeword length is represented. */ #define LZX_PRECODE_ELEMENT_SIZE 4 /* Number of low-order bits of each match offset that are entropy-encoded in * aligned offset blocks. */ #define LZX_NUM_ALIGNED_OFFSET_BITS 3 /* Number of symbols in the aligned offset code. */ #define LZX_ALIGNEDCODE_NUM_SYMBOLS (1 << LZX_NUM_ALIGNED_OFFSET_BITS) /* Mask for the match offset bits that are entropy-encoded in aligned offset * blocks. */ #define LZX_ALIGNED_OFFSET_BITMASK ((1 << LZX_NUM_ALIGNED_OFFSET_BITS) - 1) /* Number of bits in which each aligned offset codeword length is represented. */ #define LZX_ALIGNEDCODE_ELEMENT_SIZE 3 /* The first offset slot which requires an aligned offset symbol in aligned * offset blocks. */ #define LZX_MIN_ALIGNED_OFFSET_SLOT 8 /* The offset slot base for LZX_MIN_ALIGNED_OFFSET_SLOT. */ #define LZX_MIN_ALIGNED_OFFSET 14 /* The maximum number of extra offset bits in verbatim blocks. (One would need * to subtract LZX_NUM_ALIGNED_OFFSET_BITS to get the number of extra offset * bits in *aligned* blocks.) */ #define LZX_MAX_NUM_EXTRA_BITS 17 /* Maximum lengths (in bits) for length-limited Huffman code construction. */ #define LZX_MAX_MAIN_CODEWORD_LEN 16 #define LZX_MAX_LEN_CODEWORD_LEN 16 #define LZX_MAX_PRE_CODEWORD_LEN ((1 << LZX_PRECODE_ELEMENT_SIZE) - 1) #define LZX_MAX_ALIGNED_CODEWORD_LEN ((1 << LZX_ALIGNEDCODE_ELEMENT_SIZE) - 1) /* For LZX-compressed blocks in WIM resources, this value is always used as the * filesize parameter for the call instruction (0xe8 byte) preprocessing, even * though the blocks themselves are not this size, and the size of the actual * file resource in the WIM file is very likely to be something entirely * different as well. */ #define LZX_WIM_MAGIC_FILESIZE 12000000 /* Assumed LZX block size when the encoded block size begins with a 0 bit. * This is probably WIM-specific. */ #define LZX_DEFAULT_BLOCK_SIZE 32768 /* Number of offsets in the recent (or "repeat") offsets queue. */ #define LZX_NUM_RECENT_OFFSETS 3 /* An offset of n bytes is actually encoded as (n + LZX_OFFSET_ADJUSTMENT). */ #define LZX_OFFSET_ADJUSTMENT (LZX_NUM_RECENT_OFFSETS - 1) #endif /* _LZX_CONSTANTS_H */ wimlib-1.13.1/include/wimlib/header.h0000644000175000017500000001514213277475672014337 00000000000000#ifndef _WIMLIB_HEADER_H #define _WIMLIB_HEADER_H #include #include "wimlib/guid.h" #include "wimlib/resource.h" #include "wimlib/types.h" /* Length of the WIM header on disk. wimlib currently requires that the header * be exactly this size. */ #define WIM_HEADER_DISK_SIZE 208 /* Default WIM version number. Blobs are always compressed independently. */ #define WIM_VERSION_DEFAULT 0x10d00 /* Version number used for WIMs that allow multiple blobs combined into one * resource ("solid resources", marked by WIM_RESHDR_FLAG_SOLID) and also a new * compression format (LZMS). This version is new as of Windows 8 WIMGAPI. * Although it is used by Windows 8 web downloader, it is not yet documented by * Microsoft. */ #define WIM_VERSION_SOLID 0xe00 /* Note: there is another WIM version from Vista pre-releases, but it is not * supported by wimlib. */ /* WIM magic characters, translated to a single 64-bit number. */ #define WIM_MAGIC \ (((u64)'M' << 0) | \ ((u64)'S' << 8) | \ ((u64)'W' << 16) | \ ((u64)'I' << 24) | \ ((u64)'M' << 32) | \ ((u64)'\0' << 40) | \ ((u64)'\0' << 48) | \ ((u64)'\0' << 54)) /* wimlib pipable WIM magic characters, translated to a single 64-bit number. * */ #define PWM_MAGIC \ (((u64)'W' << 0) | \ ((u64)'L' << 8) | \ ((u64)'P' << 16) | \ ((u64)'W' << 24) | \ ((u64)'M' << 32) | \ ((u64)'\0' << 40) | \ ((u64)'\0' << 48) | \ ((u64)'\0' << 54)) /* On-disk format of the WIM header. */ struct wim_header_disk { /* +0x00: Magic characters WIM_MAGIC or PWM_MAGIC. */ le64 magic; /* +0x08: Size of the WIM header, in bytes; WIM_HEADER_DISK_SIZE * expected (currently the only supported value). */ le32 hdr_size; /* +0x0c: Version of the WIM file. Recognized values are the * WIM_VERSION_* constants from above. */ le32 wim_version; /* +0x10: Flags for the WIM file (WIM_HDR_FLAG_*). */ le32 wim_flags; /* +0x14: Uncompressed chunk size for non-solid compressed resources in * the WIM or 0 if the WIM is uncompressed. */ le32 chunk_size; /* +0x18: Globally unique identifier for the WIM file. Basically a * bunch of random bytes. */ u8 guid[GUID_SIZE]; /* +0x28: Number of this WIM part in the split WIM file, indexed from 1, * or 1 if the WIM is not split. */ le16 part_number; /* +0x2a: Total number of parts of the split WIM file, or 1 if the WIM * is not split. */ le16 total_parts; /* +0x2c: Number of images in the WIM. WIMGAPI requires that this be at * least 1. wimlib allows 0. */ le32 image_count; /* +0x30: Location and size of the WIM's blob table. */ struct wim_reshdr_disk blob_table_reshdr; /* +0x48: Location and size of the WIM's XML data. */ struct wim_reshdr_disk xml_data_reshdr; /* +0x60: Location and size of metadata resource for the bootable image * of the WIM, or all zeroes if no image is bootable. */ struct wim_reshdr_disk boot_metadata_reshdr; /* +0x78: 1-based index of the bootable image of the WIM, or 0 if no * image is bootable. */ le32 boot_idx; /* +0x7c: Location and size of the WIM's integrity table, or all zeroes * if the WIM has no integrity table. * * Note the integrity_table_reshdr here is 4-byte aligned even though it * would ordinarily be 8-byte aligned--- hence, the _packed_attribute on * this structure is essential. */ struct wim_reshdr_disk integrity_table_reshdr; /* +0x94: Unused bytes. */ u8 unused[60]; /* +0xd0 (208) */ } _packed_attribute; #define MAX_IMAGES (((INT_MAX < INT32_MAX) ? INT_MAX : INT32_MAX) - 1) /* In-memory representation of a WIM header. See `struct wim_header_disk' for * field descriptions. */ struct wim_header { u64 magic; u32 wim_version; u32 flags; u32 chunk_size; u8 guid[GUID_SIZE]; u16 part_number; u16 total_parts; u32 image_count; struct wim_reshdr blob_table_reshdr; struct wim_reshdr xml_data_reshdr; struct wim_reshdr boot_metadata_reshdr; u32 boot_idx; struct wim_reshdr integrity_table_reshdr; }; /* WIM header flags ('wim_flags' field of 'struct wim_header_disk'): */ /* Reserved flag. */ #define WIM_HDR_FLAG_RESERVED 0x00000001 /* The WIM may contain compressed resources --- specifically, resources with * WIM_RESHDR_FLAG_COMPRESSED set in their headers. Exactly one of the more * specific WIM_HDR_FLAG_COMPRESSION_* flags must be set to specify the * compression format used. */ #define WIM_HDR_FLAG_COMPRESSION 0x00000002 /* The WIM is read-only, so modifications should not be allowed even if the WIM * is writable at the filesystem level. */ #define WIM_HDR_FLAG_READONLY 0x00000004 /* The WIM is part of a split WIM. */ #define WIM_HDR_FLAG_SPANNED 0x00000008 /* All blobs included in the WIM's blob table are non-metadata (do not have * WIM_RESHDR_FLAG_METADATA set). wimlib ignores this flag and clears it on new * WIM files it writes. */ #define WIM_HDR_FLAG_RESOURCE_ONLY 0x00000010 /* All blobs included in the WIM's blob table are metadata (have * WIM_RESHDR_FLAG_METADATA set). wimlib ignores this flag and clears it on new * WIM files it writes. */ #define WIM_HDR_FLAG_METADATA_ONLY 0x00000020 /* The WIM is currently being written or appended to. */ #define WIM_HDR_FLAG_WRITE_IN_PROGRESS 0x00000040 /* Reparse point fixup flag. See docs for the --rpfix and --norpfix options to * 'wimlib-imagex capture', or the WIMLIB_ADD_FLAG_{RPFIX,NORPFIX} flags in * wimlib.h. Note that WIM_HDR_FLAG_RP_FIX is a header flag and just sets the * default behavior for the WIM; it can still be programatically overridden on a * per-image basis. But there is no flag in the file format to set the default * behavior for a specific image. */ #define WIM_HDR_FLAG_RP_FIX 0x00000080 /* Unused, reserved flag for another compression type. */ #define WIM_HDR_FLAG_COMPRESS_RESERVED 0x00010000 /* Resources in the WIM with WIM_RESHDR_FLAG_COMPRESSED set in their headers are * compressed with XPRESS compression. */ #define WIM_HDR_FLAG_COMPRESS_XPRESS 0x00020000 /* Resources in the WIM with WIM_RESHDR_FLAG_COMPRESSED set in their headers are * compressed with LZX compression. */ #define WIM_HDR_FLAG_COMPRESS_LZX 0x00040000 /* Resources in the WIM with WIM_RESHDR_FLAG_COMPRESSED set in their headers are * compressed with LZMS compression. Note: this flag is only valid if the WIM * version is WIM_VERSION_SOLID. Also, this flag is only supported in wimlib * v1.6.0 and later and WIMGAPI Windows 8 and later. */ #define WIM_HDR_FLAG_COMPRESS_LZMS 0x00080000 /* XPRESS, with small chunk size??? */ #define WIM_HDR_FLAG_COMPRESS_XPRESS_2 0x00200000 #endif /* _WIMLIB_HEADER_H */ wimlib-1.13.1/include/wimlib/xml_windows.h0000644000175000017500000000026213160354224015432 00000000000000#ifndef _WIMLIB_XML_WINDOWS_H #define _WIMLIB_XML_WINDOWS_H #include "wimlib/types.h" extern int set_windows_specific_info(WIMStruct *wim); #endif /* _WIMLIB_XML_WINDOWS_H */ wimlib-1.13.1/include/wimlib/security_descriptor.h0000644000175000017500000001005413160354224017165 00000000000000/* * security_descriptor.h - declarations for Windows security descriptor format * * The following copying information applies to this specific source code file: * * Written in 2013-2015 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . */ #ifndef _WIMLIB_SECURITY_DESCRIPTOR_H #define _WIMLIB_SECURITY_DESCRIPTOR_H #include "wimlib/compiler.h" #include "wimlib/types.h" /* Note: the data types in this header are prefixed with wimlib_ to avoid * conflicts with the same types being defined in the libntfs-3g headers. */ /* Windows NT security descriptor, in self-relative format */ typedef struct { /* Security descriptor revision; should be 1 */ u8 revision; /* Padding */ u8 sbz1; /* Bitwise OR of flags defined below, such as SE_DACL_PRESENT */ le16 control; /* Offset of owenr SID structure in the security descriptor */ le32 owner_offset; /* Offset of group SID structure in the security descriptor */ le32 group_offset; /* Offset of System Access Control List (SACL) in security descriptor, * or 0 if no SACL is present */ le32 sacl_offset; /* Offset of Discretionary Access Control List (DACL) in security * descriptor, or 0 if no DACL is present */ le32 dacl_offset; } _packed_attribute wimlib_SECURITY_DESCRIPTOR_RELATIVE; #define wimlib_SE_OWNER_DEFAULTED 0x0001 #define wimlib_SE_GROUP_DEFAULTED 0x0002 #define wimlib_SE_DACL_PRESENT 0x0004 #define wimlib_SE_DACL_DEFAULTED 0x0008 #define wimlib_SE_SACL_PRESENT 0x0010 #define wimlib_SE_SACL_DEFAULTED 0x0020 #define wimlib_SE_DACL_AUTO_INHERIT_REQ 0x0100 #define wimlib_SE_SACL_AUTO_INHERIT_REQ 0x0200 #define wimlib_SE_DACL_AUTO_INHERITED 0x0400 #define wimlib_SE_SACL_AUTO_INHERITED 0x0800 #define wimlib_SE_DACL_PROTECTED 0x1000 #define wimlib_SE_SACL_PROTECTED 0x2000 #define wimlib_SE_RM_CONTROL_VALID 0x4000 #define wimlib_SE_SELF_RELATIVE 0x8000 /* Windows NT security identifier (user or group) */ typedef struct { u8 revision; u8 sub_authority_count; /* Identifies the authority that issued the SID */ u8 identifier_authority[6]; le32 sub_authority[]; } _packed_attribute wimlib_SID; /* Header of a Windows NT access control list */ typedef struct { /* ACL_REVISION or ACL_REVISION_DS */ u8 revision; /* padding */ u8 sbz1; /* Total size of the ACL, including all access control entries */ le16 acl_size; /* Number of access control entry structures that follow the ACL * structure */ le16 ace_count; /* padding */ le16 sbz2; } _packed_attribute wimlib_ACL; #define wimlib_ACCESS_ALLOWED_ACE_TYPE 0 #define wimlib_ACCESS_DENIED_ACE_TYPE 1 #define wimlib_SYSTEM_AUDIT_ACE_TYPE 2 /* Header of a Windows NT access control entry */ typedef struct { /* Type of ACE */ u8 type; /* Bitwise OR of inherit ACE flags */ u8 flags; /* Size of the access control entry, including this header */ le16 size; } _packed_attribute wimlib_ACE_HEADER; /* Windows NT access control entry to grant rights to a user or group */ typedef struct { wimlib_ACE_HEADER hdr; le32 mask; wimlib_SID sid; } _packed_attribute wimlib_ACCESS_ALLOWED_ACE; /* Windows NT access control entry to deny rights to a user or group */ typedef struct { wimlib_ACE_HEADER hdr; le32 mask; wimlib_SID sid; } _packed_attribute wimlib_ACCESS_DENIED_ACE; /* Windows NT access control entry to audit access to the object */ typedef struct { wimlib_ACE_HEADER hdr; le32 mask; wimlib_SID sid; } _packed_attribute wimlib_SYSTEM_AUDIT_ACE; #endif /* _WIMLIB_SECURITY_DESCRIPTOR_H */ wimlib-1.13.1/include/wimlib/ntfs_3g.h0000644000175000017500000000115413160354224014424 00000000000000#ifndef _WIMLIB_NTFS_3G_H #define _WIMLIB_NTFS_3G_H #ifdef WITH_NTFS_3G #include "wimlib/types.h" struct blob_descriptor; struct consume_chunk_callback; struct ntfs_location; extern int read_ntfs_attribute_prefix(const struct blob_descriptor *blob, u64 size, const struct consume_chunk_callback *cb); extern struct ntfs_location * clone_ntfs_location(const struct ntfs_location *loc); extern void free_ntfs_location(struct ntfs_location *loc); extern int cmp_ntfs_locations(const struct ntfs_location *loc1, const struct ntfs_location *loc2); #endif /* WITH_NTFS_3G */ #endif /* _WIMLIB_NTFS_3G_H */ wimlib-1.13.1/include/wimlib/dentry.h0000644000175000017500000002170013160354224014365 00000000000000#ifndef _WIMLIB_DENTRY_H #define _WIMLIB_DENTRY_H #include "wimlib/avl_tree.h" #include "wimlib/case.h" #include "wimlib/compiler.h" #include "wimlib/inode.h" #include "wimlib/list.h" #include "wimlib/types.h" struct wim_inode; struct blob_table; /* Base size of a WIM dentry in the on-disk format, up to and including the file * name length. This does not include the variable-length file name, short * name, extra stream entries, and padding to 8-byte boundaries. */ #define WIM_DENTRY_DISK_SIZE 102 /* * In-memory structure for a WIM directory entry (dentry). There is a directory * tree for each image in the WIM. * * Note that this is a directory entry and not an inode. Since NTFS allows hard * links, it's possible for an NTFS inode to correspond to multiple WIM * dentries. The hard link group ID field of the on-disk WIM dentry tells us * the number of the NTFS inode that the dentry corresponds to (and this gets * placed in d_inode->i_ino). * * Unfortunately, WIM files do not have an analogue to an inode; instead certain * information, such as file attributes, the security descriptor, and streams is * replicated in each hard-linked dentry, even though this information really is * associated with an inode. In-memory, we fix up this flaw by allocating a * `struct wim_inode' for each dentry that contains some of this duplicated * information, then combining the inodes for each hard link group together. * (See dentry_tree_fix_inodes().) */ struct wim_dentry { /* Pointer to the inode for this dentry. This will contain some * information that was factored out of the on-disk WIM dentry as common * to all dentries in a hard link group. */ struct wim_inode *d_inode; /* Node for the parent's balanced binary search tree of child dentries * keyed by filename (root i_children). */ struct avl_tree_node d_index_node; /* The parent of this directory entry. (The root is its own parent.) */ struct wim_dentry *d_parent; /* Linked list node that places this dentry in the list of aliases for * its inode (d_inode) */ struct hlist_node d_alias_node; /* Pointer to the UTF-16LE filename (malloc()ed buffer), or NULL if this * dentry has no filename. */ utf16lechar *d_name; /* Pointer to the UTF-16LE short filename (malloc()ed buffer), or NULL * if this dentry has no short name. */ utf16lechar *d_short_name; /* Length of 'd_name' in bytes, excluding the terminating null */ u16 d_name_nbytes; /* Length of 'd_short_name' in bytes, excluding the terminating null */ u16 d_short_name_nbytes; /* (Extraction only) Length of 'd_extraction_name' in _characters_, * excluding the terminating null */ u16 d_extraction_name_nchars; /* When capturing from an NTFS volume using NTFS-3G, this flag is set on * dentries that were created from a filename in the WIN32 or WIN32+DOS * namespaces rather than the POSIX namespace. Otherwise this will * always be 0. */ u16 d_is_win32_name : 1; /* Temporary flag; always reset to 0 when done using. */ u16 d_tmp_flag : 1; /* Used by wimlib_update_image() */ u16 d_is_orphan : 1; union { /* The subdir offset is only used while reading and writing this * dentry. See the corresponding field in `struct * wim_dentry_on_disk' for explanation. */ u64 d_subdir_offset; /* Temporary list field */ struct list_head d_tmp_list; }; /* Full path to this dentry in the WIM, in platform-dependent tchars * that can be printed without conversion. By default this field will * be NULL and will only be calculated on-demand by the * calculate_dentry_full_path() or dentry_full_path() functions. */ tchar *d_full_path; /* (Extraction only) Actual name to extract this dentry as. This may be * either in 'tchars' or in 'utf16lechars', depending on what encoding * the extraction backend needs. This may alias 'd_name'. If it * doesn't, it is an allocated buffer which must be freed. */ void *d_extraction_name; /* (Extraction only) Linked list node that connects all dentries being * extracted as part of the current extraction operation. */ struct list_head d_extraction_list_node; /* (Extraction only) Pointer to the next alias of this dentry's inode * that needs to be extracted as part of the current extraction * operation, or NULL if this is the last alias. */ struct wim_dentry *d_next_extraction_alias; #ifdef ENABLE_TEST_SUPPORT struct wim_dentry *d_corresponding; #endif }; static inline bool will_extract_dentry(const struct wim_dentry *dentry) { return dentry->d_extraction_list_node.next != NULL; } extern size_t dentry_out_total_length(const struct wim_dentry *dentry); extern int for_dentry_in_tree(struct wim_dentry *root, int (*visitor)(struct wim_dentry *, void *), void *args); /* Iterate through each @child dentry of the @dir directory inode in * collation order. */ #define for_inode_child(child, dir) \ avl_tree_for_each_in_order((child), (dir)->i_children, \ struct wim_dentry, d_index_node) /* Iterate through each @child dentry of the @parent dentry in * collation order. */ #define for_dentry_child(child, parent) \ for_inode_child((child), (parent)->d_inode) /* Iterate through each @child dentry of the @dir directory inode in * postorder (safe for freeing the child dentries). */ #define for_inode_child_postorder(child, dir) \ avl_tree_for_each_in_postorder((child), (dir)->i_children, \ struct wim_dentry, d_index_node) /* Iterate through each @child dentry of the @parent dentry in * postorder (safe for freeing the child dentries). */ #define for_dentry_child_postorder(child, parent) \ for_inode_child_postorder((child), (parent)->d_inode) /* Get any child dentry of the @dir directory inode. Requires * inode_has_children(@dir) == true. */ #define inode_any_child(dir) \ avl_tree_entry((dir)->i_children, struct wim_dentry, d_index_node) /* Get any child dentry of the @parent dentry. Requires * dentry_has_children(@parent) == true. */ #define dentry_any_child(parent) \ inode_any_child((parent)->d_inode) extern struct wim_dentry * dentry_get_first_ci_match(struct wim_dentry *dentry); extern struct wim_dentry * dentry_get_next_ci_match(struct wim_dentry *dentry, struct wim_dentry *ci_match); /* Iterate through all other dentries which have the same case insensitive name * as the one given. */ #define dentry_for_each_ci_match(ci_match, dentry) \ for ((ci_match) = dentry_get_first_ci_match((dentry)); \ (ci_match); \ (ci_match) = dentry_get_next_ci_match((dentry), (ci_match))) extern void calculate_subdir_offsets(struct wim_dentry *root, u64 *subdir_offset_p); extern int dentry_set_name(struct wim_dentry *dentry, const tchar *name); extern int dentry_set_name_utf16le(struct wim_dentry *dentry, const utf16lechar *name, size_t name_nbytes); extern struct wim_dentry * get_dentry(WIMStruct *wim, const tchar *path, CASE_SENSITIVITY_TYPE case_type); extern struct wim_dentry * get_dentry_child_with_name(const struct wim_dentry *dentry, const tchar *name, CASE_SENSITIVITY_TYPE case_type); extern struct wim_dentry * get_dentry_child_with_utf16le_name(const struct wim_dentry *dentry, const utf16lechar *name, size_t name_nbytes, CASE_SENSITIVITY_TYPE case_type); extern struct wim_dentry * get_parent_dentry(WIMStruct *wim, const tchar *path, CASE_SENSITIVITY_TYPE case_type); extern int calculate_dentry_full_path(struct wim_dentry *dentry); extern tchar * dentry_full_path(struct wim_dentry *dentry); extern int new_dentry_with_new_inode(const tchar *name, bool set_timestamps, struct wim_dentry **dentry_ret); extern int new_dentry_with_existing_inode(const tchar *name, struct wim_inode *inode, struct wim_dentry **dentry_ret); extern int new_filler_directory(struct wim_dentry **dentry_ret); extern void free_dentry(struct wim_dentry *dentry); extern void free_dentry_tree(struct wim_dentry *root, struct blob_table *blob_table); extern void unlink_dentry(struct wim_dentry *dentry); extern struct wim_dentry * dentry_add_child(struct wim_dentry *parent, struct wim_dentry *child); struct update_command_journal; extern int rename_wim_path(WIMStruct *wim, const tchar *from, const tchar *to, CASE_SENSITIVITY_TYPE case_type, struct update_command_journal *j); extern int read_dentry_tree(const u8 *buf, size_t buf_len, u64 root_offset, struct wim_dentry **root_ret); extern u8 * write_dentry_tree(struct wim_dentry *root, u8 *p); static inline bool dentry_is_root(const struct wim_dentry *dentry) { return dentry->d_parent == dentry; } static inline bool dentry_is_directory(const struct wim_dentry *dentry) { return inode_is_directory(dentry->d_inode); } static inline bool dentry_has_children(const struct wim_dentry *dentry) { return inode_has_children(dentry->d_inode); } static inline bool dentry_has_long_name(const struct wim_dentry *dentry) { return dentry->d_name_nbytes != 0; } static inline bool dentry_has_short_name(const struct wim_dentry *dentry) { return dentry->d_short_name_nbytes != 0; } #endif /* _WIMLIB_DENTRY_H */ wimlib-1.13.1/include/wimlib/metadata.h0000644000175000017500000001413613160354224014645 00000000000000#ifndef _WIMLIB_METADATA_H #define _WIMLIB_METADATA_H #include "wimlib/blob_table.h" #include "wimlib/list.h" #include "wimlib/types.h" #include "wimlib/wim.h" /* * This structure holds the directory tree that comprises a WIM image, along * with other information maintained at the image level. It is populated either * by reading and parsing a metadata resource or by scanning new files. * * An image that hasn't been modified from its on-disk copy is considered * "clean" and is loaded from its metadata resource on demand by * select_wim_image(). Such an image may be unloaded later to save memory when * a different image is selected. An image that has been modified or has been * created from scratch, on the other hand, is considered "dirty" and is never * automatically unloaded. * * To implement exports, it's allowed that multiple WIMStructs reference the * same wim_image_metadata. */ struct wim_image_metadata { /* Number of WIMStructs that reference this image. This will always be * >= 1. It may be > 1 if this image has been exported. */ u32 refcnt; /* Number of WIMStructs that have this image selected as their * current_image. This will always be <= 'refcnt' and may be 0. */ u32 selected_refcnt; /* Pointer to the root dentry of this image, or NULL if this image is * completely empty or is not currently loaded. */ struct wim_dentry *root_dentry; /* Pointer to the security data of this image, or NULL if this image is * not currently loaded. */ struct wim_security_data *security_data; /* Pointer to the blob descriptor for this image's metadata resource. * If this image metadata is sourced from a WIM file (as opposed to * being created from scratch) and hasn't been modified from the version * in that WIM file, then this blob descriptor's data corresponds to the * WIM backing source. Otherwise, this blob descriptor is a dummy entry * with blob_location==BLOB_NONEXISTENT. */ struct blob_descriptor *metadata_blob; /* Linked list of 'struct wim_inode's for this image, or an empty list * if this image is completely empty or is not currently loaded. */ struct hlist_head inode_list; /* Linked list of 'struct blob_descriptor's for blobs that are * referenced by this image's dentry tree, but have not had their SHA-1 * message digests calculated yet and therefore have not been inserted * into the WIMStruct's blob table. This list is appended to when files * are scanned for inclusion in this WIM image. */ struct list_head unhashed_blobs; /* Are the filecount/bytecount stats (in the XML info) out of date for * this image? */ bool stats_outdated; }; /* Retrieve the metadata of the image in @wim currently selected with * select_wim_image(). */ static inline struct wim_image_metadata * wim_get_current_image_metadata(WIMStruct *wim) { return wim->image_metadata[wim->current_image - 1]; } /* Retrieve the root dentry of the image in @wim currently selected with * select_wim_image(). */ static inline struct wim_dentry * wim_get_current_root_dentry(WIMStruct *wim) { return wim_get_current_image_metadata(wim)->root_dentry; } /* Retrieve the security data of the image in @wim currently selected with * select_wim_image(). */ static inline struct wim_security_data * wim_get_current_security_data(WIMStruct *wim) { return wim_get_current_image_metadata(wim)->security_data; } /* Return true iff the specified image has been changed since being read from * its backing file or has been created from scratch. */ static inline bool is_image_dirty(const struct wim_image_metadata *imd) { /* The only possible values here are BLOB_NONEXISTENT and BLOB_IN_WIM */ return imd->metadata_blob->blob_location == BLOB_NONEXISTENT; } /* Return true iff the specified image is unchanged since being read from the * specified backing WIM file. */ static inline bool is_image_unchanged_from_wim(const struct wim_image_metadata *imd, const WIMStruct *wim) { return !is_image_dirty(imd) && imd->metadata_blob->rdesc->wim == wim; } /* Mark the metadata for the specified WIM image "dirty" following changes to * the image's directory tree. This records that the metadata no longer matches * the version in the WIM file (if any) and that its stats are out of date. */ static inline void mark_image_dirty(struct wim_image_metadata *imd) { blob_release_location(imd->metadata_blob); imd->stats_outdated = true; } /* Return true iff the specified image is currently loaded into memory. */ static inline bool is_image_loaded(const struct wim_image_metadata *imd) { /* Check security_data rather than root_dentry, since root_dentry will * be NULL for a completely empty image whereas security_data will still * be non-NULL in that case. */ return imd->security_data != NULL; } /* Return true iff it is okay to unload the specified image. The image can be * unloaded if no WIMStructs have it selected and it is not dirty. */ static inline bool can_unload_image(const struct wim_image_metadata *imd) { return imd->selected_refcnt == 0 && !is_image_dirty(imd); } /* Iterate over each inode in a WIM image */ #define image_for_each_inode(inode, imd) \ hlist_for_each_entry(inode, &(imd)->inode_list, i_hlist_node) /* Iterate over each inode in a WIM image (safe against inode removal) */ #define image_for_each_inode_safe(inode, tmp, imd) \ hlist_for_each_entry_safe(inode, tmp, &(imd)->inode_list, i_hlist_node) /* Iterate over each blob in a WIM image that has not yet been hashed */ #define image_for_each_unhashed_blob(blob, imd) \ list_for_each_entry(blob, &(imd)->unhashed_blobs, unhashed_list) /* Iterate over each blob in a WIM image that has not yet been hashed (safe * against blob removal) */ #define image_for_each_unhashed_blob_safe(blob, tmp, imd) \ list_for_each_entry_safe(blob, tmp, &(imd)->unhashed_blobs, unhashed_list) extern void put_image_metadata(struct wim_image_metadata *imd); extern int append_image_metadata(WIMStruct *wim, struct wim_image_metadata *imd); extern struct wim_image_metadata * new_empty_image_metadata(void); extern struct wim_image_metadata * new_unloaded_image_metadata(struct blob_descriptor *metadata_blob); #endif /* _WIMLIB_METADATA_H */ wimlib-1.13.1/include/wimlib/list.h0000644000175000017500000002177013160354224014042 00000000000000 /* * This file is based on include/linux/list.h in the Linux kernel source code. */ #ifndef _WIMLIB_LIST_H #define _WIMLIB_LIST_H #include #include /* Simple doubly linked list implementation. */ struct list_head { struct list_head *next; struct list_head *prev; }; #define LIST_HEAD_INIT(name) { &(name), &(name) } #undef LIST_HEAD /* BSD sys/queue.h defines this... */ #define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) static inline void INIT_LIST_HEAD(struct list_head *list) { list->next = list; list->prev = list; } /* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } /** * list_add - add a new entry * @new: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } /** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } /** * list_replace - replace old entry by new one * @old : the element to be replaced * @new : the new element to insert * * If @old was empty, it will be overwritten. */ static inline void list_replace(struct list_head *old, struct list_head *new) { new->next = old->next; new->next->prev = new; new->prev = old->prev; new->prev->next = new; } /** * list_del - deletes entry from list. * @entry: the element to delete from the list. * Note: list_empty() on entry does not return true after this, the entry is * in an undefined state. */ static inline void list_del(struct list_head *entry) { struct list_head *prev = entry->prev; struct list_head *next = entry->next; prev->next = next; next->prev = prev; } /** * list_empty - tests whether a list is empty * @head: the list to test. */ static inline bool list_empty(const struct list_head *head) { return head->next == head; } static inline void __list_splice(const struct list_head *list, struct list_head *prev, struct list_head *next) { struct list_head *first = list->next; struct list_head *last = list->prev; first->prev = prev; prev->next = first; last->next = next; next->prev = last; } /** * list_splice - join two lists, this is designed for stacks * @list: the new list to add. * @head: the place to add it in the first list. */ static inline void list_splice(const struct list_head *list, struct list_head *head) { if (!list_empty(list)) __list_splice(list, head, head->next); } /** * list_move - delete from one list and add as another's head * @list: the entry to move * @head: the head that will precede our entry */ static inline void list_move(struct list_head *list, struct list_head *head) { list_del(list); list_add(list, head); } /** * list_move_tail - delete from one list and add as another's tail * @list: the entry to move * @head: the head that will follow our entry */ static inline void list_move_tail(struct list_head *list, struct list_head *head) { list_del(list); list_add_tail(list, head); } /** * list_splice_tail - join two lists, each list being a queue * @list: the new list to add. * @head: the place to add it in the first list. */ static inline void list_splice_tail(struct list_head *list, struct list_head *head) { if (!list_empty(list)) __list_splice(list, head->prev, head); } /** * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. */ #define list_entry(ptr, type, member) \ container_of(ptr, type, member) /** * list_first_entry - get the first element from a list * @ptr: the list head to take the element from. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. * * Note, that list is expected to be not empty. */ #define list_first_entry(ptr, type, member) \ list_entry((ptr)->next, type, member) /** * list_last_entry - get the last element from a list * @ptr: the list head to take the element from. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. * * Note, that list is expected to be not empty. */ #define list_last_entry(ptr, type, member) \ list_entry((ptr)->prev, type, member) /** * list_next_entry - get the next element in list * @pos: the type * to cursor * @member: the name of the list_struct within the struct. */ #define list_next_entry(pos, member) \ list_entry((pos)->member.next, typeof(*(pos)), member) /** * list_prev_entry - get the prev element in list * @pos: the type * to cursor * @member: the name of the list_struct within the struct. */ #define list_prev_entry(pos, member) \ list_entry((pos)->member.prev, typeof(*(pos)), member) /** * list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop cursor. * @head: the head for your list. */ #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) /** * list_for_each_entry - iterate over list of given type * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry(pos, head, member) \ for (pos = list_first_entry(head, typeof(*pos), member); \ &pos->member != (head); \ pos = list_next_entry(pos, member)) /** * list_for_each_entry_reverse - iterate backwards over list of given type. * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_reverse(pos, head, member) \ for (pos = list_last_entry(head, typeof(*pos), member); \ &pos->member != (head); \ pos = list_prev_entry(pos, member)) /** * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_safe(pos, n, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member), \ n = list_entry(pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry(n->member.next, typeof(*n), member)) /* * Double linked lists with a single pointer list head. * Mostly useful for hash tables where the two pointer list head is * too wasteful. * You lose the ability to access the tail in O(1). */ struct hlist_head { struct hlist_node *first; }; struct hlist_node { struct hlist_node *next; struct hlist_node **pprev; }; static inline void INIT_HLIST_HEAD(struct hlist_head *h) { h->first = NULL; } static inline bool hlist_unhashed(const struct hlist_node *h) { return !h->pprev; } static inline bool hlist_empty(const struct hlist_head *h) { return !h->first; } static inline void hlist_del(struct hlist_node *n) { struct hlist_node *next = n->next; struct hlist_node **pprev = n->pprev; *pprev = next; if (next) next->pprev = pprev; } static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) { struct hlist_node *first = h->first; n->next = first; if (first) first->pprev = &n->next; h->first = n; n->pprev = &h->first; } #define hlist_entry(ptr, type, member) container_of(ptr,type,member) #define hlist_entry_safe(ptr, type, member) \ ({ typeof(ptr) ____ptr = (ptr); \ ____ptr ? hlist_entry(____ptr, type, member) : NULL; \ }) /** * hlist_for_each_entry - iterate over list of given type * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry(pos, head, member) \ for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member);\ pos; \ pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member)) /** * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry * @pos: the type * to use as a loop cursor. * @n: another &struct hlist_node to use as temporary storage * @head: the head for your list. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry_safe(pos, n, head, member) \ for (pos = hlist_entry_safe((head)->first, typeof(*pos), member);\ pos && ({ n = pos->member.next; 1; }); \ pos = hlist_entry_safe(n, typeof(*pos), member)) #endif /* _WIMLIB_LIST_H */ wimlib-1.13.1/include/wimlib/decompressor_ops.h0000644000175000017500000000131613277475672016473 00000000000000/* * decompressor_ops.h * * Interface implemented by decompressors for specific formats. */ #ifndef _WIMLIB_DECOMPRESSOR_OPS_H #define _WIMLIB_DECOMPRESSOR_OPS_H #include struct decompressor_ops { int (*create_decompressor)(size_t max_block_size, void **private_ret); int (*decompress)(const void *compressed_data, size_t compressed_size, void *uncompressed_data, size_t uncompressed_size, void *private); void (*free_decompressor)(void *private); }; extern const struct decompressor_ops lzx_decompressor_ops; extern const struct decompressor_ops xpress_decompressor_ops; extern const struct decompressor_ops lzms_decompressor_ops; #endif /* _WIMLIB_DECOMPRESSOR_OPS_H */ wimlib-1.13.1/include/wimlib/progress.h0000644000175000017500000000500013160354224014717 00000000000000#ifndef _WIMLIB_PROGRESS_H #define _WIMLIB_PROGRESS_H #include "wimlib.h" #include "wimlib/paths.h" #include "wimlib/types.h" /* If specified, call the user-provided progress function and check its result. */ static inline int call_progress(wimlib_progress_func_t progfunc, enum wimlib_progress_msg msg, union wimlib_progress_info *info, void *progctx) { if (progfunc) { enum wimlib_progress_status status; status = (*progfunc)(msg, info, progctx); switch (status) { case WIMLIB_PROGRESS_STATUS_CONTINUE: return 0; case WIMLIB_PROGRESS_STATUS_ABORT: return WIMLIB_ERR_ABORTED_BY_PROGRESS; default: return WIMLIB_ERR_UNKNOWN_PROGRESS_STATUS; } } return 0; } extern int report_error(wimlib_progress_func_t progfunc, void *progctx, int error_code, const tchar *path); /* Rate-limiting of byte-count based progress messages: update *next_progress_p * to the value that completed_bytes needs to reach before the next progress * message will be sent. */ static inline void set_next_progress(u64 completed_bytes, u64 total_bytes, u64 *next_progress_p) { if (*next_progress_p < total_bytes) { /* * Send the next message as soon as: * - another 1/128 of the total has been processed; * - OR another 5000000 bytes have been processed; * - OR all bytes have been processed. */ *next_progress_p = min(min(completed_bytes + total_bytes / 128, completed_bytes + 5000000), total_bytes); } else { /* Last message has been sent. */ *next_progress_p = ~0; } } /* Windows: temporarily remove the stream name from the path */ static inline tchar * progress_get_streamless_path(const tchar *path) { tchar *cookie = NULL; #ifdef __WIN32__ cookie = (wchar_t *)path_stream_name(path); if (cookie) *--cookie = L'\0'; /* Overwrite the colon */ #endif return cookie; } /* Windows: temporarily replace \??\ with \\?\ (to make an NT namespace path * into a Win32 namespace path) */ static inline tchar * progress_get_win32_path(const tchar *path) { #ifdef __WIN32__ if (!wcsncmp(path, L"\\??\\", 4)) { ((wchar_t *)path)[1] = L'\\'; return (wchar_t *)&path[1]; } #endif return NULL; } /* Windows: restore the NT namespace path */ static inline void progress_put_win32_path(tchar *cookie) { #ifdef __WIN32__ if (cookie) *cookie = L'?'; #endif } /* Windows: restore the stream name part of the path */ static inline void progress_put_streamless_path(tchar *cookie) { #ifdef __WIN32__ if (cookie) *cookie = L':'; #endif } #endif /* _WIMLIB_PROGRESS_H */ wimlib-1.13.1/include/wimlib/inode_table.h0000644000175000017500000000263013160354224015326 00000000000000#ifndef _WIMLIB_INODE_TABLE_H #define _WIMLIB_INODE_TABLE_H #include "wimlib/list.h" #include "wimlib/types.h" #include "wimlib/util.h" struct wim_dentry; /* Hash table to find inodes for hard link detection, given an inode number (in * the case of reading a WIM image), or both an inode number and a device number * (in the case of adding files to a WIM image). Also contains an extra list to * hold inodes for which no additional hard link detection is desired. In both * cases the inodes are linked by i_hlist_node. */ struct wim_inode_table { struct hlist_head *array; size_t filled; size_t capacity; struct hlist_head extra_inodes; }; /* Compute the index of the hash bucket to use for the given inode number and * device number. */ static inline size_t hash_inode(const struct wim_inode_table *table, u64 ino, u64 devno) { return (hash_u64(ino) + devno) & (table->capacity - 1); } extern int init_inode_table(struct wim_inode_table *table, size_t capacity); extern int inode_table_new_dentry(struct wim_inode_table *table, const tchar *name, u64 ino, u64 devno, bool noshare, struct wim_dentry **dentry_ret); extern void enlarge_inode_table(struct wim_inode_table *table); extern void inode_table_prepare_inode_list(struct wim_inode_table *table, struct hlist_head *head); extern void destroy_inode_table(struct wim_inode_table *table); #endif /* _WIMLIB_INODE_TABLE_H */ wimlib-1.13.1/include/wimlib/write.h0000644000175000017500000000374513160354224014223 00000000000000#ifndef _WIMLIB_WRITE_H #define _WIMLIB_WRITE_H #include "wimlib.h" #include "wimlib/types.h" /* Internal use only */ #define WIMLIB_WRITE_FLAG_FILE_DESCRIPTOR 0x80000000 #define WIMLIB_WRITE_FLAG_APPEND 0x40000000 #define WIMLIB_WRITE_FLAG_NO_NEW_BLOBS 0x20000000 #define WIMLIB_WRITE_FLAG_USE_EXISTING_TOTALBYTES 0x10000000 #define WIMLIB_WRITE_FLAG_NO_METADATA 0x08000000 /* Keep in sync with wimlib.h */ #define WIMLIB_WRITE_MASK_PUBLIC ( \ WIMLIB_WRITE_FLAG_CHECK_INTEGRITY | \ WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY | \ WIMLIB_WRITE_FLAG_PIPABLE | \ WIMLIB_WRITE_FLAG_NOT_PIPABLE | \ WIMLIB_WRITE_FLAG_RECOMPRESS | \ WIMLIB_WRITE_FLAG_FSYNC | \ WIMLIB_WRITE_FLAG_REBUILD | \ WIMLIB_WRITE_FLAG_SOFT_DELETE | \ WIMLIB_WRITE_FLAG_IGNORE_READONLY_FLAG | \ WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS | \ WIMLIB_WRITE_FLAG_STREAMS_OK | \ WIMLIB_WRITE_FLAG_RETAIN_GUID | \ WIMLIB_WRITE_FLAG_SOLID | \ WIMLIB_WRITE_FLAG_SEND_DONE_WITH_FILE_MESSAGES | \ WIMLIB_WRITE_FLAG_NO_SOLID_SORT | \ WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) #if defined(HAVE_SYS_FILE_H) && defined(HAVE_FLOCK) extern int lock_wim_for_append(WIMStruct *wim); extern void unlock_wim_for_append(WIMStruct *wim); #else static inline int lock_wim_for_append(WIMStruct *wim) { return 0; } static inline void unlock_wim_for_append(WIMStruct *wim) { } #endif struct filedes; struct list_head; struct wim_reshdr; int write_wim_part(WIMStruct *wim, const void *path_or_fd, int image, int write_flags, unsigned num_threads, unsigned part_number, unsigned total_parts, struct list_head *blob_list_override, const u8 *guid); int write_wim_resource_from_buffer(const void *buf, size_t buf_size, bool is_metadata, struct filedes *out_fd, int out_ctype, u32 out_chunk_size, struct wim_reshdr *out_reshdr, u8 *hash_ret, int write_resource_flags); #endif /* _WIMLIB_WRITE_H */ wimlib-1.13.1/include/wimlib/resource.h0000644000175000017500000002336613160354224014721 00000000000000#ifndef _WIMLIB_RESOURCE_H #define _WIMLIB_RESOURCE_H #include "wimlib/list.h" #include "wimlib/sha1.h" #include "wimlib/types.h" struct blob_descriptor; struct filedes; struct wim_image_metadata; /* * Description of a "resource" in a WIM file. A "resource" is a standalone, * possibly compressed region of data. Normally, there is a one-to-one * correspondence between "blobs" (each of which may be the contents of a file, * for example) and resources. However, a resource with the * WIM_RESHDR_FLAG_SOLID flag set is a "solid" resource that contains multiple * blobs compressed together. */ struct wim_resource_descriptor { /* The WIM containing this resource. @wim->in_fd is expected to be a * file descriptor to the underlying WIM file, opened for reading. */ WIMStruct *wim; /* The offset, in bytes, from the start of WIM file at which this * resource starts. */ u64 offset_in_wim; /* The size of this resource in the WIM file. For compressed resources * this is the compressed size, including overhead such as the chunk * table. */ u64 size_in_wim; /* The number of bytes of uncompressed data this resource decompresses * to. */ u64 uncompressed_size; /* The list of blobs this resource contains. */ struct list_head blob_list; /* Flags for this resource (WIM_RESHDR_FLAG_*). */ u32 flags : 8; /* [wimlib extension] This flag will be set if the WIM is pipable. In * such cases, the resource will be in a slightly different format if it * is compressed. */ u32 is_pipable : 1; /* Temporary flag. */ u32 raw_copy_ok : 1; /* Compression type of this resource. */ u32 compression_type : 22; /* Compression chunk size of this resource. Irrelevant if the resource * is uncompressed. */ u32 chunk_size; }; /* On-disk version of a WIM resource header. */ struct wim_reshdr_disk { /* Size of the resource as it appears in the WIM file (possibly * compressed). */ u8 size_in_wim[7]; /* Zero or more of the WIM_RESHDR_FLAG_* flags. These indicate, for * example, whether the resource is compressed or not. */ u8 flags; /* Offset of the resource from the start of the WIM file, in bytes. */ le64 offset_in_wim; /* Uncompressed size of the resource, in bytes. */ le64 uncompressed_size; } _packed_attribute; /* In-memory version of a WIM resource header (`struct wim_reshdr_disk'). */ struct wim_reshdr { u64 size_in_wim : 56; u64 flags : 8; u64 offset_in_wim; u64 uncompressed_size; }; /* Flags for the `flags' field of WIM resource headers (`struct wim_reshdr'). */ /* Unknown meaning; currently ignored by wimlib. */ #define WIM_RESHDR_FLAG_FREE 0x01 /* The resource is a metadata resource for a WIM image, or is the blob table or * XML data for the WIM. */ #define WIM_RESHDR_FLAG_METADATA 0x02 /* The resource is a non-solid resource compressed using the WIM's default * compression type. */ #define WIM_RESHDR_FLAG_COMPRESSED 0x04 /* Unknown meaning; currently ignored by wimlib. */ #define WIM_RESHDR_FLAG_SPANNED 0x08 /* The resource is a solid compressed resource which may contain multiple blobs. * This flag is only allowed if the WIM version number is WIM_VERSION_SOLID. */ #define WIM_RESHDR_FLAG_SOLID 0x10 /* Magic number in the 'uncompressed_size' field of the resource header that * identifies the main entry for a solid resource. */ #define SOLID_RESOURCE_MAGIC_NUMBER 0x100000000ULL static inline void copy_reshdr(struct wim_reshdr *dest, const struct wim_reshdr *src) { memcpy(dest, src, sizeof(struct wim_reshdr)); } static inline void zero_reshdr(struct wim_reshdr *reshdr) { memset(reshdr, 0, sizeof(struct wim_reshdr)); } extern void wim_reshdr_to_desc(const struct wim_reshdr *reshdr, WIMStruct *wim, struct wim_resource_descriptor *rdesc); extern void wim_reshdr_to_desc_and_blob(const struct wim_reshdr *reshdr, WIMStruct *wim, struct wim_resource_descriptor *rdesc, struct blob_descriptor *blob); extern void get_wim_reshdr(const struct wim_reshdr_disk *disk_reshdr, struct wim_reshdr *reshdr); extern void put_wim_reshdr(const struct wim_reshdr *reshdr, struct wim_reshdr_disk *disk_reshdr); /* Alternate chunk table format for resources with WIM_RESHDR_FLAG_SOLID set. */ struct alt_chunk_table_header_disk { /* Uncompressed size of the resource in bytes. */ le64 res_usize; /* Number of bytes each compressed chunk decompresses into, except * possibly the last which decompresses into the remainder. This * overrides the chunk size specified by the WIM header. */ le32 chunk_size; /* Compression format used for compressed chunks: * 0 = None * 1 = XPRESS * 2 = LZX * 3 = LZMS * * This overrides the compression type specified by the WIM header. */ le32 compression_format; /* This header is directly followed by a table of compressed sizes of * the chunks (4 bytes per entry). */ } _packed_attribute; static inline unsigned int get_chunk_entry_size(u64 res_size, bool is_alt) { if (res_size <= UINT32_MAX || is_alt) return 4; else return 8; } /* Functions to read blobs */ extern int read_partial_wim_blob_into_buf(const struct blob_descriptor *blob, u64 offset, size_t size, void *buf); extern int read_blob_into_buf(const struct blob_descriptor *blob, void *buf); extern int read_blob_into_alloc_buf(const struct blob_descriptor *blob, void **buf_ret); extern int wim_reshdr_to_data(const struct wim_reshdr *reshdr, WIMStruct *wim, void **buf_ret); extern int wim_reshdr_to_hash(const struct wim_reshdr *reshdr, WIMStruct *wim, u8 hash[SHA1_HASH_SIZE]); extern int skip_wim_resource(const struct wim_resource_descriptor *rdesc); /* * Callback function for reading chunks. Called whenever the next chunk of * uncompressed data is available, passing 'ctx' as the last argument. 'size' is * guaranteed to be nonzero. Must return 0 on success, or a positive wimlib * error code on failure. */ struct consume_chunk_callback { int (*func)(const void *chunk, size_t size, void *ctx); void *ctx; }; /* Pass a chunk of data to the specified consume_chunk callback */ static inline int consume_chunk(const struct consume_chunk_callback *cb, const void *chunk, size_t size) { return (*cb->func)(chunk, size, cb->ctx); } /* Callback functions for reading blobs */ struct read_blob_callbacks { /* Called when starting to read a blob. Must return 0 on success, or a * positive wimlib error code on failure, or in the case of * read_blob_list(), the special value BEGIN_BLOB_STATUS_SKIP_BLOB which * indicates that the data for this blob should not be read. */ int (*begin_blob)(struct blob_descriptor *blob, void *ctx); #define BEGIN_BLOB_STATUS_SKIP_BLOB (-1) /* Called when the next chunk of uncompressed data is available. 'size' * is guaranteed to be nonzero. Must return 0 on success, or a positive * wimlib error code on failure. */ int (*continue_blob)(const struct blob_descriptor *blob, u64 offset, const void *chunk, size_t size, void *ctx); /* Called when a blob has been successfully read (status=0), or when * begin_blob() was successfully called but an error occurred before the * blob was fully read (status != 0; in this case the implementation * should do cleanup and then pass on the status). Must return 0 on * success, or a positive wimlib error code on failure. */ int (*end_blob)(struct blob_descriptor *blob, int status, void *ctx); /* Parameter passed to each of the callback functions. */ void *ctx; }; /* Call cbs->begin_blob() if present. */ static inline int call_begin_blob(struct blob_descriptor *blob, const struct read_blob_callbacks *cbs) { if (!cbs->begin_blob) return 0; return (*cbs->begin_blob)(blob, cbs->ctx); } /* Call cbs->continue_blob() if present. */ static inline int call_continue_blob(const struct blob_descriptor *blob, u64 offset, const void *chunk, size_t size, const struct read_blob_callbacks *cbs) { if (!cbs->continue_blob) return 0; return (*cbs->continue_blob)(blob, offset, chunk, size, cbs->ctx); } /* Call cbs->end_blob() if present. */ static inline int call_end_blob(struct blob_descriptor *blob, int status, const struct read_blob_callbacks *cbs) { if (!cbs->end_blob) return status; return (*cbs->end_blob)(blob, status, cbs->ctx); } /* Flags for read_blob_list() */ #define VERIFY_BLOB_HASHES 0x1 #define COMPUTE_MISSING_BLOB_HASHES 0x2 #define BLOB_LIST_ALREADY_SORTED 0x4 extern int read_blob_list(struct list_head *blob_list, size_t list_head_offset, const struct read_blob_callbacks *cbs, int flags); extern int read_blob_with_cbs(struct blob_descriptor *blob, const struct read_blob_callbacks *cbs); extern int read_blob_with_sha1(struct blob_descriptor *blob, const struct read_blob_callbacks *cbs); extern int extract_blob_prefix_to_fd(struct blob_descriptor *blob, u64 size, struct filedes *fd); extern int extract_blob_to_fd(struct blob_descriptor *blob, struct filedes *fd); /* Miscellaneous blob functions. */ extern int sha1_blob(struct blob_descriptor *blob); /* Functions to read/write metadata resources. */ extern int read_metadata_resource(struct wim_image_metadata *imd); extern int write_metadata_resource(WIMStruct *wim, int image, int write_resource_flags); /* Definitions specific to pipable WIM resources. */ /* Arbitrary number to begin each blob in the pipable WIM, used for sanity * checking. */ #define PWM_BLOB_MAGIC 0x2b9b9ba2443db9d8ULL /* Header that precedes each blob in a pipable WIM. */ struct pwm_blob_hdr { le64 magic; /* +0 */ le64 uncompressed_size; /* +8 */ u8 hash[SHA1_HASH_SIZE]; /* +16 */ le32 flags; /* +36 */ /* +40 */ } _packed_attribute; /* Header that precedes each chunk of a compressed resource in a pipable WIM. */ struct pwm_chunk_hdr { le32 compressed_size; } _packed_attribute; #endif /* _WIMLIB_RESOURCE_H */ wimlib-1.13.1/include/wimlib/security.h0000644000175000017500000000344713160354224014737 00000000000000#ifndef _WIMLIB_SECURITY_H #define _WIMLIB_SECURITY_H #include "wimlib/types.h" struct wim_security_data; struct avl_tree_node; /* Map from SHA1 message digests of security descriptors to security IDs, which * are themselves indices into the table of security descriptors in the 'struct * wim_security_data'. */ struct wim_sd_set { struct wim_security_data *sd; struct avl_tree_node *root; s32 orig_num_entries; }; /* Table of security descriptors for a WIM image. */ struct wim_security_data { /* The total length of the security data, in bytes. If there are no * security descriptors, this field, when read from the on-disk metadata * resource, may be either 8 (which is correct) or 0 (which is * interpreted as 8). */ u32 total_length; /* The number of security descriptors in the array @descriptors. */ u32 num_entries; /* Array of sizes of the descriptors, in bytes, in the array * @descriptors. */ u64 *sizes; /* Array of pointers to the security descriptors in the * SECURITY_DESCRIPTOR_RELATIVE format. */ u8 **descriptors; }; extern void rollback_new_security_descriptors(struct wim_sd_set *sd_set); extern void destroy_sd_set(struct wim_sd_set *sd_set); extern s32 sd_set_add_sd(struct wim_sd_set *sd_set, const char descriptor[], size_t size); extern int init_sd_set(struct wim_sd_set *sd_set, struct wim_security_data *sd); extern struct wim_security_data * new_wim_security_data(void); extern int read_wim_security_data(const u8 *buf, size_t buf_len, struct wim_security_data **sd_ret); extern u8 * write_wim_security_data(const struct wim_security_data * restrict sd, u8 * restrict p); extern void print_wim_security_data(const struct wim_security_data *sd); extern void free_wim_security_data(struct wim_security_data *sd); #endif /* _WIMLIB_SECURITY_H */ wimlib-1.13.1/include/wimlib/file_io.h0000644000175000017500000000230013160354224014461 00000000000000#ifndef _WIMLIB_FILE_IO_H #define _WIMLIB_FILE_IO_H #include #include #include /* Wrapper around a file descriptor that keeps track of offset (including in * pipes, which don't support lseek()) and a cached flag that tells whether the * file descriptor is a pipe or not. */ struct filedes { int fd; unsigned int is_pipe : 1; off_t offset; }; extern int full_read(struct filedes *fd, void *buf, size_t n); extern int full_pread(struct filedes *fd, void *buf, size_t nbyte, off_t offset); extern int full_write(struct filedes *fd, const void *buf, size_t n); extern int full_pwrite(struct filedes *fd, const void *buf, size_t count, off_t offset); #ifndef __WIN32__ # define O_BINARY 0 #endif extern off_t filedes_seek(struct filedes *fd, off_t offset); extern bool filedes_is_seekable(struct filedes *fd); static inline void filedes_init(struct filedes *fd, int raw_fd) { fd->fd = raw_fd; fd->offset = 0; fd->is_pipe = 0; } static inline void filedes_invalidate(struct filedes *fd) { fd->fd = -1; } #define filedes_close(f) close((f)->fd) static inline bool filedes_valid(const struct filedes *fd) { return fd->fd != -1; } #endif /* _WIMLIB_FILE_IO_H */ wimlib-1.13.1/include/wimlib/compiler.h0000644000175000017500000001657213160354224014705 00000000000000/* * compiler.h * * Compiler-specific definitions. Currently, only GCC and clang are supported. * * The following copying information applies to this specific source code file: * * Written in 2013-2016 by Eric Biggers * * To the extent possible under law, the author(s) have dedicated all copyright * and related and neighboring rights to this software to the public domain * worldwide via the Creative Commons Zero 1.0 Universal Public Domain * Dedication (the "CC0"). * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the CC0 for more details. * * You should have received a copy of the CC0 along with this software; if not * see . */ #ifndef _WIMLIB_COMPILER_H #define _WIMLIB_COMPILER_H /* Is the compiler GCC of the specified version or later? This always returns * false for clang, since clang is "frozen" at GNUC 4.2. The __has_* * feature-test macros should be used to detect clang functionality instead. */ #define GCC_PREREQ(major, minor) \ (!defined(__clang__) && !defined(__INTEL_COMPILER) && \ (__GNUC__ > major || \ (__GNUC__ == major && __GNUC_MINOR__ >= minor))) /* Feature-test macros defined by recent versions of clang. */ #ifndef __has_attribute # define __has_attribute(attribute) 0 #endif #ifndef __has_feature # define __has_feature(feature) 0 #endif #ifndef __has_builtin # define __has_builtin(builtin) 0 #endif /* Declare that the annotated function should be exported from the shared * library (or DLL). */ #ifdef __WIN32__ # define WIMLIBAPI __declspec(dllexport) #else # define WIMLIBAPI __attribute__((visibility("default"))) #endif /* Declare that the annotated function should always be inlined. This might be * desirable in highly tuned code, e.g. compression codecs. */ #define forceinline inline __attribute__((always_inline)) /* Declare that the annotated function should *not* be inlined. */ #define noinline __attribute__((noinline)) /* Functionally the same as 'noinline', but documents that the reason for not * inlining is to prevent the annotated function from being inlined into a * recursive function, thereby increasing its stack usage. */ #define noinline_for_stack noinline /* Hint that the expression is usually true. */ #define likely(expr) __builtin_expect(!!(expr), 1) /* Hint that the expression is usually false. */ #define unlikely(expr) __builtin_expect(!!(expr), 0) /* Prefetch into L1 cache for read. */ #define prefetchr(addr) __builtin_prefetch((addr), 0) /* Prefetch into L1 cache for write. */ #define prefetchw(addr) __builtin_prefetch((addr), 1) /* Declare that the members of the annotated struct are tightly packed, and the * struct itself may be misaligned. */ #define _packed_attribute __attribute__((packed)) /* Declare that the annotated variable, or variables of the annotated type, are * to be aligned on n-byte boundaries. */ #define _aligned_attribute(n) __attribute__((aligned(n))) /* Declare that pointers to the annotated type may alias other pointers. */ #define _may_alias_attribute __attribute__((may_alias)) /* Hint that the annotated function is rarely called. */ #if GCC_PREREQ(4, 4) || __has_attribute(cold) # define _cold_attribute __attribute__((cold)) #else # define _cold_attribute #endif /* Hint that the annotated function is malloc-like: any non-null pointer it * returns will not alias any pointer previously in use by the program. */ #define _malloc_attribute __attribute__((malloc)) /* Hint that the annotated function takes a printf()-like format string and * arguments. This is currently disabled on Windows because MinGW does not * support this attribute on functions taking wide-character strings. */ #ifdef __WIN32__ # define _format_attribute(type, format_str, format_start) #else # define _format_attribute(type, format_str, format_start) \ __attribute__((format(type, format_str, format_start))) #endif /* Hint that the annotated function is intentionally not used. This might be * the case if the function contains only static assertions. */ #define _unused_attribute __attribute__((unused)) /* Endianness definitions. Either CPU_IS_BIG_ENDIAN or CPU_IS_LITTLE_ENDIAN is * set to 1. The other is set to 0. Note that newer gcc supports * __BYTE_ORDER__ for easily determining the endianness; older gcc doesn't. In * the latter case we fall back to a configure-time check. */ #ifdef __BYTE_ORDER__ # define CPU_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) #elif defined(HAVE_CONFIG_H) # include "config.h" # ifdef WORDS_BIGENDIAN # define CPU_IS_BIG_ENDIAN 1 # else # define CPU_IS_BIG_ENDIAN 0 # endif #endif #define CPU_IS_LITTLE_ENDIAN (!CPU_IS_BIG_ENDIAN) /* UNALIGNED_ACCESS_IS_FAST should be defined to 1 if unaligned memory accesses * can be performed efficiently on the target platform. */ #if defined(__x86_64__) || defined(__i386__) || defined(__ARM_FEATURE_UNALIGNED) # define UNALIGNED_ACCESS_IS_FAST 1 #else # define UNALIGNED_ACCESS_IS_FAST 0 #endif /* Get the type of the specified expression. */ #define typeof __typeof__ /* Get the minimum of two variables, without multiple evaluation. */ #ifndef min # define min(a, b) ({ typeof(a) _a = (a); typeof(b) _b = (b); \ (_a < _b) ? _a : _b; }) #endif /* Get the maximum of two variables, without multiple evaluation. */ #ifndef max # define max(a, b) ({ typeof(a) _a = (a); typeof(b) _b = (b); \ (_a > _b) ? _a : _b; }) #endif /* Swap the values of two variables, without multiple evaluation. */ #ifndef swap # define swap(a, b) ({ typeof(a) _a = (a); (a) = (b); (b) = _a; }) #endif /* (Optional) Efficiently swap the bytes of a 16-bit integer. */ #if GCC_PREREQ(4, 8) || __has_builtin(__builtin_bswap16) # define compiler_bswap16 __builtin_bswap16 #endif /* (Optional) Efficiently swap the bytes of a 32-bit integer. */ #if GCC_PREREQ(4, 3) || __has_builtin(__builtin_bswap32) # define compiler_bswap32 __builtin_bswap32 #endif /* (Optional) Efficiently swap the bytes of a 64-bit integer. */ #if GCC_PREREQ(4, 3) || __has_builtin(__builtin_bswap64) # define compiler_bswap64 __builtin_bswap64 #endif /* (Optional) Find Last Set bit and Find First Set bit macros. */ #define compiler_bsr32(n) (31 - __builtin_clz(n)) #define compiler_bsr64(n) (63 - __builtin_clzll(n)) #define compiler_bsf32(n) __builtin_ctz(n) #define compiler_bsf64(n) __builtin_ctzll(n) /* Optional definitions for checking with 'sparse'. */ #ifdef __CHECKER__ # define _bitwise_attr __attribute__((bitwise)) # define _force_attr __attribute__((force)) #else # define _bitwise_attr # define _force_attr #endif /* STATIC_ASSERT() - verify the truth of an expression at compilation time. */ #ifdef __CHECKER__ # define STATIC_ASSERT(expr) #elif __STDC_VERSION__ >= 201112L # define STATIC_ASSERT(expr) _Static_assert((expr), "") #else # define STATIC_ASSERT(expr) ((void)sizeof(char[1 - 2 * !(expr)])) #endif /* STATIC_ASSERT_ZERO() - verify the truth of an expression at compilation time * and also produce a result of value '0' to be used in constant expressions */ #define STATIC_ASSERT_ZERO(expr) ((int)sizeof(char[-!(expr)])) #define CONCAT_IMPL(s1, s2) s1##s2 /* CONCAT() - concatenate two tokens at preprocessing time. */ #define CONCAT(s1, s2) CONCAT_IMPL(s1, s2) #endif /* _WIMLIB_COMPILER_H */ wimlib-1.13.1/include/wimlib/xattr.h0000644000175000017500000001261013272231267014227 00000000000000#ifndef _WIMLIB_XATTR_H #define _WIMLIB_XATTR_H #include #include "wimlib/endianness.h" #include "wimlib/tagged_items.h" #include "wimlib/util.h" #undef HAVE_LINUX_XATTR_SUPPORT #if defined(HAVE_SYS_XATTR_H) && \ defined(HAVE_LLISTXATTR) && defined(HAVE_LGETXATTR) && \ defined(HAVE_FSETXATTR) && defined(HAVE_LSETXATTR) # define HAVE_LINUX_XATTR_SUPPORT 1 #endif #define WIM_XATTR_NAME_MAX 255 #define WIM_XATTR_SIZE_MAX 65535 /* * On-disk format of each extended attribute (xattr, or EA) entry in a metadata * item tagged with TAG_XATTRS. This is the preferred xattr format, since it is * also used by WIMGAPI and DISM starting in Windows 10 version 1607. */ struct wim_xattr_entry { /* length of xattr value in bytes */ le16 value_len; /* length of xattr name in bytes, excluding the null terminator */ u8 name_len; /* flags: 0 or 0x80 (FILE_NEED_EA) */ u8 flags; /* followed by the xattr name *with* a null terminator */ char name[0]; /* followed by the xattr value */ /* u8 value[0]; */ /* no padding at end! */ } _packed_attribute; static inline size_t xattr_entry_size(const struct wim_xattr_entry *entry) { STATIC_ASSERT(sizeof(*entry) == 4); return sizeof(*entry) + entry->name_len + 1 + le16_to_cpu(entry->value_len); } /* minimum is a 1-byte name (plus null terminator) and an empty value */ #define XATTR_ENTRY_MIN_SIZE (sizeof(struct wim_xattr_entry) + 2) static inline struct wim_xattr_entry * xattr_entry_next(const struct wim_xattr_entry *entry) { return (void *)entry + xattr_entry_size(entry); } static inline bool valid_xattr_entry(const struct wim_xattr_entry *entry, size_t avail) { if (avail < sizeof(*entry)) return false; return entry->name_len > 0 && entry->name_len <= WIM_XATTR_NAME_MAX && le16_to_cpu(entry->value_len) <= WIM_XATTR_SIZE_MAX && avail >= xattr_entry_size(entry) && memchr(entry->name, '\0', entry->name_len) == NULL && entry->name[entry->name_len] == '\0'; } /* * On-disk format of each extended attribute entry in a metadata item tagged * with TAG_WIMLIB_LINUX_XATTRS. This is a deprecated format which wimlib * v1.11-v1.12 used to store extended attributes on Linux (predating the Windows * xattr support in both WIMGAPI and wimlib). Now we use TAG_XATTRS for both * Windows and Linux xattrs. */ struct wimlib_xattr_entry_old { /* length of xattr name in bytes, excluding a null terminator */ le16 name_len; /* reserved, must be 0 */ le16 reserved; /* length of xattr value in bytes */ le32 value_len; /* followed by the xattr name *without* a null terminator */ char name[0]; /* followed by the xattr value */ /* u8 value[0]; */ /* then zero-padded to a 4-byte boundary */ } _aligned_attribute(4); static inline size_t old_xattr_entry_size(const struct wimlib_xattr_entry_old *entry) { STATIC_ASSERT(sizeof(*entry) == 8); return ALIGN(sizeof(*entry) + le16_to_cpu(entry->name_len) + le32_to_cpu(entry->value_len), 4); } /* minimum is a 1-byte name and an empty value */ #define OLD_XATTR_ENTRY_MIN_SIZE \ (ALIGN(sizeof(struct wimlib_xattr_entry_old) + 1, 4)) static inline struct wimlib_xattr_entry_old * old_xattr_entry_next(const struct wimlib_xattr_entry_old *entry) { return (void *)entry + old_xattr_entry_size(entry); } static inline bool old_valid_xattr_entry(const struct wimlib_xattr_entry_old *entry, size_t avail) { u16 name_len; if (avail < sizeof(*entry)) return false; name_len = le16_to_cpu(entry->name_len); return name_len > 0 && name_len <= WIM_XATTR_NAME_MAX && le32_to_cpu(entry->value_len) <= WIM_XATTR_SIZE_MAX && avail >= old_xattr_entry_size(entry) && memchr(entry->name, '\0', name_len) == NULL; } /* Is the xattr of the specified name security-related on Linux? */ static inline bool is_linux_security_xattr(const char *name) { #define XATTR_SECURITY_PREFIX "security." #define XATTR_SYSTEM_PREFIX "system." #define XATTR_POSIX_ACL_ACCESS "posix_acl_access" #define XATTR_NAME_POSIX_ACL_ACCESS XATTR_SYSTEM_PREFIX XATTR_POSIX_ACL_ACCESS #define XATTR_POSIX_ACL_DEFAULT "posix_acl_default" #define XATTR_NAME_POSIX_ACL_DEFAULT XATTR_SYSTEM_PREFIX XATTR_POSIX_ACL_DEFAULT return !strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) || !strcmp(name, XATTR_NAME_POSIX_ACL_ACCESS) || !strcmp(name, XATTR_NAME_POSIX_ACL_DEFAULT); } static inline const void * inode_get_xattrs(const struct wim_inode *inode, u32 *len_ret) { return inode_get_tagged_item(inode, TAG_XATTRS, XATTR_ENTRY_MIN_SIZE, len_ret); } static inline const void * inode_get_xattrs_old(const struct wim_inode *inode, u32 *len_ret) { return inode_get_tagged_item(inode, TAG_WIMLIB_LINUX_XATTRS, OLD_XATTR_ENTRY_MIN_SIZE, len_ret); } static inline const void * inode_get_linux_xattrs(const struct wim_inode *inode, u32 *len_ret, bool *is_old_format_ret) { const void *entries; entries = inode_get_xattrs(inode, len_ret); if (entries) { *is_old_format_ret = false; return entries; } entries = inode_get_xattrs_old(inode, len_ret); if (entries) { *is_old_format_ret = true; return entries; } return NULL; } static inline bool inode_has_xattrs(const struct wim_inode *inode) { return inode_get_xattrs(inode, NULL) != NULL || inode_get_xattrs_old(inode, NULL) != NULL; } static inline bool inode_set_xattrs(struct wim_inode *inode, const void *entries, u32 len) { return inode_set_tagged_item(inode, TAG_XATTRS, entries, len); } #endif /* _WIMLIB_XATTR_H */ wimlib-1.13.1/include/wimlib_tchar.h0000644000175000017500000001005113160354224014236 00000000000000#ifndef _WIMLIB_TCHAR_H #define _WIMLIB_TCHAR_H /* Functions to act on "tchar" strings, which have a platform-dependent encoding * and character size. */ #ifdef __WIN32__ #include /* * For Windows builds, the "tchar" type will be 2 bytes and will be equivalent * to "wchar_t" and "utf16lechar". All indicate one coding unit of a string * encoded in UTF-16LE with the additional possibility of unpaired surrogates. */ typedef wchar_t tchar; # define TCHAR_IS_UTF16LE 1 # define _T(text) L##text # define T(text) _T(text) /* Make a string literal into a wide string */ # define TS "ls" /* Format a string of "tchar" */ # define TC "lc" /* Format a "tchar" */ /* For Windows builds, the following definitions replace the "tchar" functions * with the "wide-character" functions. */ # define tmemchr wmemchr # define tmemcpy wmemcpy # define tmemmove wmemmove # define tmempcpy wmempcpy # define tstrcat wcscat # define tstrcpy wcscpy # define tprintf wprintf # define tsprintf swprintf # define tfprintf fwprintf # define tvfprintf vfwprintf # define tscanf swscanf # define istalpha(c) iswalpha((wchar_t)(c)) # define istspace(c) iswspace((wchar_t)(c)) # define totlower(c) towlower((wchar_t)(c)) # define tstrcmp wcscmp # define tstrncmp wcsncmp # define tstrchr wcschr # define tstrpbrk wcspbrk # define tstrrchr wcsrchr # define tstrlen wcslen # define tmemcmp wmemcmp # define tstrcasecmp _wcsicmp # define tstrftime wcsftime # define tputchar putwchar # define tputc putwc # define tputs _putws # define tfputs fputws # define tfopen _wfopen # define topen _wopen # define tstat _wstati64 # define tstrtol wcstol # define tstrtod wcstod # define tstrtoul wcstoul # define tstrtoull wcstoull # define tunlink _wunlink # define tstrerror _wcserror # define taccess _waccess # define tstrdup wcsdup # define tgetenv _wgetenv /* The following "tchar" functions do not have exact wide-character equivalents * on Windows so require parameter rearrangement or redirection to a replacement * function defined ourselves. */ # define TSTRDUP WCSDUP # define tmkdir(path, mode) _wmkdir(path) # define tstrerror_r win32_strerror_r_replacement # define trename win32_rename_replacement # define tglob win32_wglob #else /* __WIN32__ */ /* * For non-Windows builds, the "tchar" type will be one byte and will specify a * string encoded in UTF-8 with the additional possibility of surrogate * codepoints. */ typedef char tchar; # define TCHAR_IS_UTF16LE 0 # define T(text) text /* In this case, strings of "tchar" are simply strings of char */ # define TS "s" /* Similarly, a string of "tchar" is printed just as a normal string. */ # define TC "c" /* Print a single character */ /* For non-Windows builds, replace the "tchar" functions with the regular old * string functions. */ # define tmemchr memchr # define tmemcpy memcpy # define tmemmove memmove # define tmempcpy mempcpy # define tstrcat strcat # define tstrcpy strcpy # define tprintf printf # define tsprintf sprintf # define tfprintf fprintf # define tvfprintf vfprintf # define tscanf sscanf # define istalpha(c) isalpha((unsigned char)(c)) # define istspace(c) isspace((unsigned char)(c)) # define totlower(c) tolower((unsigned char)(c)) # define tstrcmp strcmp # define tstrncmp strncmp # define tstrchr strchr # define tstrpbrk strpbrk # define tstrrchr strrchr # define tstrlen strlen # define tmemcmp memcmp # define tstrcasecmp strcasecmp # define tstrftime strftime # define tputchar putchar # define tputc putc # define tputs puts # define tfputs fputs # define tfopen fopen # define topen open # define tstat stat # define tunlink unlink # define tstrerror strerror # define tstrtol strtol # define tstrtod strtod # define tstrtoul strtoul # define tstrtoull strtoull # define tmkdir mkdir # define tstrdup strdup # define tgetenv getenv # define TSTRDUP STRDUP # define tstrerror_r strerror_r # define trename rename # define taccess access # define tglob glob #endif /* !__WIN32__ */ #endif /* _WIMLIB_TCHAR_H */ wimlib-1.13.1/include/wimlib.h0000644000175000017500000060325113464166215013077 00000000000000/** * @file wimlib.h * @brief External header for wimlib. * * This file contains comments for generating documentation with Doxygen. The * built HTML documentation can be viewed at https://wimlib.net/apidoc. Make * sure to see the Modules page to make more sense of * the declarations in this header. */ /** * @mainpage * * This is the documentation for the library interface of wimlib 1.13.1, a C * library for creating, modifying, extracting, and mounting files in the * Windows Imaging (WIM) format. This documentation is intended for developers * only. If you have installed wimlib and want to know how to use the @b * wimlib-imagex program, please see the manual pages and also the README file. * * @section sec_installing Installing * * @subsection UNIX * * Download the source code from https://wimlib.net. Install the library by * running configure && make && sudo make install. See the README for * information about configuration options. To use wimlib in your program after * installing it, include wimlib.h and link your program with -lwim. * * @subsection Windows * * Download the Windows binary distribution with the appropriate architecture * (i686 or x86_64 --- also called "x86" and "amd64" respectively) from * https://wimlib.net. Link your program with libwim-15.dll. If needed by your * programming language or development environment, the import library * libwim.lib and C/C++ header wimlib.h can be found in the directory "devel" in * the ZIP file. * * If you need to access the DLL from non-C/C++ programming languages, note that * the calling convention is "cdecl". * * If you want to build wimlib from source on Windows, see README.WINDOWS. This * is only needed if you are making modifications to wimlib. * * @section sec_examples Examples * * Several examples are located in the "examples" directory of the source * distribution. Also see @ref sec_basic_wim_handling_concepts below. * * There is also the * source code of wimlib-imagex, which is complicated but uses most * capabilities of wimlib. * * @section backward_compatibility Backward Compatibility * * New releases of wimlib are intended to be backward compatible with old * releases, except when the libtool "age" is reset. This most recently * occurred for the v1.7.0 (libwim15) release (June 2014). Since the library is * becoming increasingly stable, the goal is to maintain the current API/ABI for * as long as possible unless there is a strong reason not to. * * As with any other library, applications should not rely on internal * implementation details that may be subject to change. * * @section sec_basic_wim_handling_concepts Basic WIM handling concepts * * wimlib wraps up a WIM file in an opaque ::WIMStruct structure. There are * two ways to create such a structure: * * 1. wimlib_open_wim() opens an on-disk WIM file and creates a ::WIMStruct for * it. * 2. wimlib_create_new_wim() creates a new ::WIMStruct that initially contains * no images and does not yet have a backing on-disk file. * * A ::WIMStruct contains zero or more independent directory trees called @a * images. Images may be extracted, added, deleted, exported, and updated using * various API functions. (See @ref G_extracting_wims and @ref G_modifying_wims * for more details.) * * Changes made to a WIM represented by a ::WIMStruct have no persistent effect * until the WIM is actually written to an on-disk file. This can be done using * wimlib_write(), but if the WIM was originally opened using wimlib_open_wim(), * then wimlib_overwrite() can be used instead. (See @ref * G_writing_and_overwriting_wims for more details.) * * wimlib's API is designed to let you combine functions to accomplish tasks in * a flexible way. Here are some example sequences of function calls: * * Apply an image from a WIM file, similar to the command-line program * wimapply: * * 1. wimlib_open_wim() * 2. wimlib_extract_image() * * Capture an image into a new WIM file, similar to wimcapture: * * 1. wimlib_create_new_wim() * 2. wimlib_add_image() * 3. wimlib_write() * * Append an image to an existing WIM file, similar to wimappend: * * 1. wimlib_open_wim() * 2. wimlib_add_image() * 3. wimlib_overwrite() * * Delete an image from an existing WIM file, similar to wimdelete: * * 1. wimlib_open_wim() * 2. wimlib_delete_image() * 3. wimlib_overwrite() * * Export an image from one WIM file to another, similar to wimexport: * * 1. wimlib_open_wim() (on source) * 2. wimlib_open_wim() (on destination) * 3. wimlib_export_image() * 4. wimlib_overwrite() (on destination) * * The API also lets you do things the command-line tools don't directly allow. * For example, you could make multiple changes to a WIM before efficiently * committing the changes with just one call to wimlib_overwrite(). Perhaps you * want to both delete an image and add a new one; or perhaps you want to * customize an image with wimlib_update_image() after adding it. All these use * cases are supported by the API. * * @section sec_cleaning_up Cleaning up * * After you are done with any ::WIMStruct, you can call wimlib_free() to free * all resources associated with it. Also, when you are completely done with * using wimlib in your program, you can call wimlib_global_cleanup() to free * any other resources allocated by the library. * * @section sec_error_handling Error Handling * * Most functions in wimlib return 0 on success and a positive * ::wimlib_error_code value on failure. Use wimlib_get_error_string() to get a * string that describes an error code. wimlib also can print error messages to * standard error or a custom file when an error occurs, and these may be more * informative than the error code; to enable this, call * wimlib_set_print_errors(). Please note that this is for convenience only, * and some errors can occur without a message being printed. Currently, error * messages and strings (as well as all documentation, for that matter) are only * available in English. * * @section sec_encodings Character encoding * * To support Windows as well as UNIX-like systems, wimlib's API typically takes * and returns strings of ::wimlib_tchar which have a platform-dependent type * and encoding. * * On Windows, each ::wimlib_tchar is a 2-byte wchar_t. The encoding * is meant to be UTF-16LE. However, unpaired surrogates are permitted because * neither Windows nor the NTFS filesystem forbids them in filenames. * * On UNIX-like systems, each ::wimlib_tchar is a 1 byte char. The * encoding is meant to be UTF-8. However, for compatibility with Windows-style * filenames that are not valid UTF-16LE, surrogate codepoints are permitted. * Other multibyte encodings (e.g. ISO-8859-1) or garbage sequences of bytes are * not permitted. * * @section sec_advanced Additional information and features * * * @subsection subsec_mounting_wim_images Mounting WIM images * * See @ref G_mounting_wim_images. * * @subsection subsec_progress_functions Progress Messages * * See @ref G_progress. * * @subsection subsec_non_standalone_wims Non-standalone WIMs * * See @ref G_nonstandalone_wims. * * @subsection subsec_pipable_wims Pipable WIMs * * wimlib supports a special "pipable" WIM format which unfortunately is @b not * compatible with Microsoft's software. To create a pipable WIM, call * wimlib_write(), wimlib_write_to_fd(), or wimlib_overwrite() with * ::WIMLIB_WRITE_FLAG_PIPABLE specified. Pipable WIMs are pipable in both * directions, so wimlib_write_to_fd() can be used to write a pipable WIM to a * pipe, and wimlib_extract_image_from_pipe() can be used to apply an image from * a pipable WIM. wimlib can also transparently open and operate on pipable WIM * s using a seekable file descriptor using the regular function calls (e.g. * wimlib_open_wim(), wimlib_extract_image()). * * See the documentation for the --pipable flag of wimcapture for * more information about pipable WIMs. * * @subsection subsec_thread_safety Thread Safety * * A ::WIMStruct is not thread-safe and cannot be accessed by multiple threads * concurrently, even for "read-only" operations such as extraction. However, * users are free to use different ::WIMStruct's from different threads * concurrently. It is even allowed for multiple ::WIMStruct's to be backed by * the same on-disk WIM file, although "overwrites" should never be done in such * a scenario. * * In addition, several functions change global state and should only be called * when a single thread is active in the library. These functions are: * * - wimlib_global_init() * - wimlib_global_cleanup() * - wimlib_set_memory_allocator() * - wimlib_set_print_errors() * - wimlib_set_error_file() * - wimlib_set_error_file_by_name() * * @subsection subsec_limitations Limitations * * This section documents some technical limitations of wimlib not already * described in the documentation for @b wimlib-imagex. * * - The old WIM format from Vista pre-releases is not supported. * - wimlib does not provide a clone of the @b PEImg tool, or the @b DISM * functionality other than that already present in @b ImageX, that allows you * to make certain Windows-specific modifications to a Windows PE image, such * as adding a driver or Windows component. Such a tool could be implemented * on top of wimlib. * * @subsection more_info More information * * You are advised to read the README as well as the documentation for * wimlib-imagex, since not all relevant information is repeated here in * the API documentation. */ /** @defgroup G_general General * * @brief Declarations and structures shared across the library. */ /** @defgroup G_creating_and_opening_wims Creating and Opening WIMs * * @brief Open an existing WIM file as a ::WIMStruct, or create a new * ::WIMStruct which can be used to create a new WIM file. */ /** @defgroup G_wim_information Retrieving WIM information and directory listings * * @brief Retrieve information about a WIM or WIM image. */ /** @defgroup G_modifying_wims Modifying WIMs * * @brief Make changes to a ::WIMStruct, in preparation of persisting the * ::WIMStruct to an on-disk file. * * @section sec_adding_images Capturing and adding WIM images * * As described in @ref sec_basic_wim_handling_concepts, capturing a new WIM or * appending an image to an existing WIM is a multi-step process, but at its * core is wimlib_add_image() or an equivalent function. Normally, * wimlib_add_image() takes an on-disk directory tree and logically adds it to a * ::WIMStruct as a new image. However, when supported by the build of the * library, there is also a special NTFS volume capture mode (entered when * ::WIMLIB_ADD_FLAG_NTFS is specified) that allows adding the image directly * from an unmounted NTFS volume. * * Another function, wimlib_add_image_multisource() is also provided. It * generalizes wimlib_add_image() to allow combining multiple files or directory * trees into a single WIM image in a configurable way. * * For maximum customization of WIM image creation, it is also possible to add a * completely empty WIM image with wimlib_add_empty_image(), then update it with * wimlib_update_image(). (This is in fact what wimlib_add_image() and * wimlib_add_image_multisource() do internally.) * * Note that some details of how image addition/capture works are documented * more fully in the documentation for wimcapture. * * @section sec_deleting_images Deleting WIM images * * wimlib_delete_image() can delete an image from a ::WIMStruct. But as usual, * wimlib_write() or wimlib_overwrite() must be called to cause the changes to * be made persistent in an on-disk WIM file. * * @section sec_exporting_images Exporting WIM images * * wimlib_export_image() can copy, or "export", an image from one WIM to * another. * * @section sec_other_modifications Other modifications * * wimlib_update_image() can add, delete, and rename files in a WIM image. * * wimlib_set_image_property() can change other image metadata. * * wimlib_set_wim_info() can change information about the WIM file itself, such * as the boot index. */ /** @defgroup G_extracting_wims Extracting WIMs * * @brief Extract files, directories, and images from a WIM. * * wimlib_extract_image() extracts, or "applies", an image from a WIM, * represented by a ::WIMStruct. This normally extracts the image to a * directory, but when supported by the build of the library there is also a * special NTFS volume extraction mode (entered when ::WIMLIB_EXTRACT_FLAG_NTFS * is specified) that allows extracting a WIM image directly to an unmounted * NTFS volume. Various other flags allow further customization of image * extraction. * * wimlib_extract_paths() and wimlib_extract_pathlist() allow extracting a list * of (possibly wildcard) paths from a WIM image. * * wimlib_extract_image_from_pipe() extracts an image from a pipable WIM sent * over a pipe; see @ref subsec_pipable_wims. * * Some details of how WIM extraction works are described more fully in the * documentation for wimapply and wimextract. */ /** @defgroup G_mounting_wim_images Mounting WIM images * * @brief Mount and unmount WIM images. * * On Linux, wimlib supports mounting images from WIM files either read-only or * read-write. To mount an image, call wimlib_mount_image(). To unmount an * image, call wimlib_unmount_image(). Mounting can be done without root * privileges because it is implemented using FUSE (Filesystem in Userspace). * * If wimlib is compiled using the --without-fuse flag, these functions * will be available but will fail with ::WIMLIB_ERR_UNSUPPORTED. * * Note: if mounting is unsupported, wimlib still provides another way to modify * a WIM image (wimlib_update_image()). */ /** * @defgroup G_progress Progress Messages * * @brief Track the progress of long WIM operations. * * Library users can provide a progress function which will be called * periodically during operations such as extracting a WIM image or writing a * WIM image. A ::WIMStruct can have a progress function of type * ::wimlib_progress_func_t associated with it by calling * wimlib_register_progress_function() or by opening the ::WIMStruct using * wimlib_open_wim_with_progress(). Once this is done, the progress function * will be called automatically during many operations, such as * wimlib_extract_image() and wimlib_write(). * * Some functions that do not operate directly on a user-provided ::WIMStruct, * such as wimlib_join(), also take the progress function directly using an * extended version of the function, such as wimlib_join_with_progress(). * * Since wimlib v1.7.0, progress functions are no longer just unidirectional. * You can now return ::WIMLIB_PROGRESS_STATUS_ABORT to cause the current * operation to be aborted. wimlib v1.7.0 also added the third argument to * ::wimlib_progress_func_t, which is a user-supplied context. */ /** @defgroup G_writing_and_overwriting_wims Writing and Overwriting WIMs * * @brief Create or update an on-disk WIM file. * * wimlib_write() creates a new on-disk WIM file, whereas wimlib_overwrite() * updates an existing WIM file. See @ref sec_basic_wim_handling_concepts for * more information about the API design. */ /** @defgroup G_nonstandalone_wims Creating and handling non-standalone WIMs * * @brief Create and handle non-standalone WIMs, such as split and delta WIMs. * * A ::WIMStruct backed by an on-disk file normally represents a fully * standalone WIM archive. However, WIM archives can also be arranged in * non-standalone ways, such as a set of on-disk files that together form a * single "split WIM" or "delta WIM". Such arrangements are fully supported by * wimlib. However, as a result, in such cases a ::WIMStruct created from one * of these on-disk files initially only partially represents the full WIM and * needs to, in effect, be logically combined with other ::WIMStruct's before * performing certain operations, such as extracting files with * wimlib_extract_image() or wimlib_extract_paths(). This is done by calling * wimlib_reference_resource_files() or wimlib_reference_resources(). Note: if * you fail to do so, you may see the error code * ::WIMLIB_ERR_RESOURCE_NOT_FOUND; this just indicates that data is not * available because the appropriate WIM files have not yet been referenced. * * wimlib_write() can create delta WIMs as well as standalone WIMs, but a * specialized function (wimlib_split()) is needed to create a split WIM. */ #ifndef _WIMLIB_H #define _WIMLIB_H #include #include #ifndef __cplusplus # if defined(_MSC_VER) && _MSC_VER < 1800 /* VS pre-2013? */ typedef unsigned char bool; # else # include # endif #endif #include #include /** @addtogroup G_general * @{ */ /** Major version of the library (for example, the 1 in 1.2.5). */ #define WIMLIB_MAJOR_VERSION 1 /** Minor version of the library (for example, the 2 in 1.2.5). */ #define WIMLIB_MINOR_VERSION 13 /** Patch version of the library (for example, the 5 in 1.2.5). */ #define WIMLIB_PATCH_VERSION 1 #ifdef __cplusplus extern "C" { #endif /* * To represent file timestamps, wimlib's API originally used the POSIX 'struct * timespec'. This was a mistake because when building wimlib for 32-bit * Windows with MinGW we ended up originally using 32-bit time_t which isn't * year 2038-safe, and therefore we had to later add fields like * 'creation_time_high' to hold the high 32 bits of each timestamp. Moreover, * old Visual Studio versions did not define struct timespec, while newer ones * define it but with 64-bit tv_sec. So to at least avoid a missing or * incompatible 'struct timespec' definition, define the correct struct * ourselves when this header is included on Windows. */ #ifdef _WIN32 struct wimlib_timespec { /* Seconds since start of UNIX epoch (January 1, 1970) */ #ifdef _WIN64 int64_t tv_sec; #else int32_t tv_sec; #endif /* Nanoseconds (0-999999999) */ int32_t tv_nsec; }; #else # define wimlib_timespec timespec /* standard definition */ #endif /** * Opaque structure that represents a WIM, possibly backed by an on-disk file. * See @ref sec_basic_wim_handling_concepts for more information. */ #ifndef WIMLIB_WIMSTRUCT_DECLARED typedef struct WIMStruct WIMStruct; #define WIMLIB_WIMSTRUCT_DECLARED #endif #ifdef _WIN32 typedef wchar_t wimlib_tchar; #else /** See @ref sec_encodings */ typedef char wimlib_tchar; #endif #ifdef _WIN32 /** Path separator for WIM paths passed back to progress callbacks. * This is forward slash on UNIX and backslash on Windows. */ # define WIMLIB_WIM_PATH_SEPARATOR '\\' # define WIMLIB_WIM_PATH_SEPARATOR_STRING L"\\" #else /** Path separator for WIM paths passed back to progress callbacks. * This is forward slash on UNIX and backslash on Windows. */ # define WIMLIB_WIM_PATH_SEPARATOR '/' # define WIMLIB_WIM_PATH_SEPARATOR_STRING "/" #endif /** A string containing a single path separator; use this to specify the root * directory of a WIM image. */ #define WIMLIB_WIM_ROOT_PATH WIMLIB_WIM_PATH_SEPARATOR_STRING /** Use this to test if the specified path refers to the root directory of the * WIM image. */ #define WIMLIB_IS_WIM_ROOT_PATH(path) \ ((path)[0] == WIMLIB_WIM_PATH_SEPARATOR && \ (path)[1] == 0) /** Length of a Globally Unique Identifier (GUID), in bytes. */ #define WIMLIB_GUID_LEN 16 /** * Specifies a compression type. * * A WIM file has a default compression type, indicated by its file header. * Normally, each resource in the WIM file is compressed with this compression * type. However, resources may be stored as uncompressed; for example, wimlib * may do so if a resource does not compress to less than its original size. In * addition, a WIM with the new version number of 3584, or "ESD file", might * contain solid resources with different compression types. */ enum wimlib_compression_type { /** * No compression. * * This is a valid argument to wimlib_create_new_wim() and * wimlib_set_output_compression_type(), but not to the functions in the * compression API such as wimlib_create_compressor(). */ WIMLIB_COMPRESSION_TYPE_NONE = 0, /** * The XPRESS compression format. This format combines Lempel-Ziv * factorization with Huffman encoding. Compression and decompression * are both fast. This format supports chunk sizes that are powers of 2 * between 2^12 and 2^16, inclusively. * * wimlib's XPRESS compressor will, with the default settings, usually * produce a better compression ratio, and work more quickly, than the * implementation in Microsoft's WIMGAPI (as of Windows 8.1). * Non-default compression levels are also supported. For example, * level 80 will enable two-pass optimal parsing, which is significantly * slower but usually improves compression by several percent over the * default level of 50. * * If using wimlib_create_compressor() to create an XPRESS compressor * directly, the @p max_block_size parameter may be any positive value * up to and including 2^16. */ WIMLIB_COMPRESSION_TYPE_XPRESS = 1, /** * The LZX compression format. This format combines Lempel-Ziv * factorization with Huffman encoding, but with more features and * complexity than XPRESS. Compression is slow to somewhat fast, * depending on the settings. Decompression is fast but slower than * XPRESS. This format supports chunk sizes that are powers of 2 * between 2^15 and 2^21, inclusively. Note: chunk sizes * other than 2^15 are not compatible with the Microsoft * implementation. * * wimlib's LZX compressor will, with the default settings, usually * produce a better compression ratio, and work more quickly, than the * implementation in Microsoft's WIMGAPI (as of Windows 8.1). * Non-default compression levels are also supported. For example, * level 20 will provide fast compression, almost as fast as XPRESS. * * If using wimlib_create_compressor() to create an LZX compressor * directly, the @p max_block_size parameter may be any positive value * up to and including 2^21. */ WIMLIB_COMPRESSION_TYPE_LZX = 2, /** * The LZMS compression format. This format combines Lempel-Ziv * factorization with adaptive Huffman encoding and range coding. * Compression and decompression are both fairly slow. This format * supports chunk sizes that are powers of 2 between 2^15 and * 2^30, inclusively. This format is best used for large chunk * sizes. Note: LZMS compression is only compatible with wimlib v1.6.0 * and later, WIMGAPI Windows 8 and later, and DISM Windows 8.1 and * later. Also, chunk sizes larger than 2^26 are not compatible * with the Microsoft implementation. * * wimlib's LZMS compressor will, with the default settings, usually * produce a better compression ratio, and work more quickly, than the * implementation in Microsoft's WIMGAPI (as of Windows 8.1). There is * limited support for non-default compression levels, but compression * will be noticeably faster if you choose a level < 35. * * If using wimlib_create_compressor() to create an LZMS compressor * directly, the @p max_block_size parameter may be any positive value * up to and including 2^30. */ WIMLIB_COMPRESSION_TYPE_LZMS = 3, }; /** @} */ /** @addtogroup G_progress * @{ */ /** Possible values of the first parameter to the user-supplied * ::wimlib_progress_func_t progress function */ enum wimlib_progress_msg { /** A WIM image is about to be extracted. @p info will point to * ::wimlib_progress_info.extract. This message is received once per * image for calls to wimlib_extract_image() and * wimlib_extract_image_from_pipe(). */ WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN = 0, /** One or more file or directory trees within a WIM image is about to * be extracted. @p info will point to ::wimlib_progress_info.extract. * This message is received only once per wimlib_extract_paths() and * wimlib_extract_pathlist(), since wimlib combines all paths into a * single extraction operation for optimization purposes. */ WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN = 1, /** This message may be sent periodically (not for every file) while * files and directories are being created, prior to file data * extraction. @p info will point to ::wimlib_progress_info.extract. * In particular, the @p current_file_count and @p end_file_count * members may be used to track the progress of this phase of * extraction. */ WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE = 3, /** File data is currently being extracted. @p info will point to * ::wimlib_progress_info.extract. This is the main message to track * the progress of an extraction operation. */ WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS = 4, /** Starting to read a new part of a split pipable WIM over the pipe. * @p info will point to ::wimlib_progress_info.extract. */ WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN = 5, /** This message may be sent periodically (not necessarily for every * file) while file and directory metadata is being extracted, following * file data extraction. @p info will point to * ::wimlib_progress_info.extract. The @p current_file_count and @p * end_file_count members may be used to track the progress of this * phase of extraction. */ WIMLIB_PROGRESS_MSG_EXTRACT_METADATA = 6, /** The image has been successfully extracted. @p info will point to * ::wimlib_progress_info.extract. This is paired with * ::WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN. */ WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END = 7, /** The files or directory trees have been successfully extracted. @p * info will point to ::wimlib_progress_info.extract. This is paired * with ::WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN. */ WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END = 8, /** The directory or NTFS volume is about to be scanned for metadata. * @p info will point to ::wimlib_progress_info.scan. This message is * received once per call to wimlib_add_image(), or once per capture * source passed to wimlib_add_image_multisource(), or once per add * command passed to wimlib_update_image(). */ WIMLIB_PROGRESS_MSG_SCAN_BEGIN = 9, /** A directory or file has been scanned. @p info will point to * ::wimlib_progress_info.scan, and its @p cur_path member will be * valid. This message is only sent if ::WIMLIB_ADD_FLAG_VERBOSE has * been specified. */ WIMLIB_PROGRESS_MSG_SCAN_DENTRY = 10, /** The directory or NTFS volume has been successfully scanned. @p info * will point to ::wimlib_progress_info.scan. This is paired with a * previous ::WIMLIB_PROGRESS_MSG_SCAN_BEGIN message, possibly with many * intervening ::WIMLIB_PROGRESS_MSG_SCAN_DENTRY messages. */ WIMLIB_PROGRESS_MSG_SCAN_END = 11, /** File data is currently being written to the WIM. @p info will point * to ::wimlib_progress_info.write_streams. This message may be * received many times while the WIM file is being written or appended * to with wimlib_write(), wimlib_overwrite(), or wimlib_write_to_fd(). */ WIMLIB_PROGRESS_MSG_WRITE_STREAMS = 12, /** Per-image metadata is about to be written to the WIM file. @p info * will not be valid. */ WIMLIB_PROGRESS_MSG_WRITE_METADATA_BEGIN = 13, /** The per-image metadata has been written to the WIM file. @p info * will not be valid. This message is paired with a preceding * ::WIMLIB_PROGRESS_MSG_WRITE_METADATA_BEGIN message. */ WIMLIB_PROGRESS_MSG_WRITE_METADATA_END = 14, /** wimlib_overwrite() has successfully renamed the temporary file to * the original WIM file, thereby committing the changes to the WIM * file. @p info will point to ::wimlib_progress_info.rename. Note: * this message is not received if wimlib_overwrite() chose to append to * the WIM file in-place. */ WIMLIB_PROGRESS_MSG_RENAME = 15, /** The contents of the WIM file are being checked against the integrity * table. @p info will point to ::wimlib_progress_info.integrity. This * message is only received (and may be received many times) when * wimlib_open_wim_with_progress() is called with the * ::WIMLIB_OPEN_FLAG_CHECK_INTEGRITY flag. */ WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY = 16, /** An integrity table is being calculated for the WIM being written. * @p info will point to ::wimlib_progress_info.integrity. This message * is only received (and may be received many times) when a WIM file is * being written with the flag ::WIMLIB_WRITE_FLAG_CHECK_INTEGRITY. */ WIMLIB_PROGRESS_MSG_CALC_INTEGRITY = 17, /** A wimlib_split() operation is in progress, and a new split part is * about to be started. @p info will point to * ::wimlib_progress_info.split. */ WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART = 19, /** A wimlib_split() operation is in progress, and a split part has been * finished. @p info will point to ::wimlib_progress_info.split. */ WIMLIB_PROGRESS_MSG_SPLIT_END_PART = 20, /** A WIM update command is about to be executed. @p info will point to * ::wimlib_progress_info.update. This message is received once per * update command when wimlib_update_image() is called with the flag * ::WIMLIB_UPDATE_FLAG_SEND_PROGRESS. */ WIMLIB_PROGRESS_MSG_UPDATE_BEGIN_COMMAND = 21, /** A WIM update command has been executed. @p info will point to * ::wimlib_progress_info.update. This message is received once per * update command when wimlib_update_image() is called with the flag * ::WIMLIB_UPDATE_FLAG_SEND_PROGRESS. */ WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND = 22, /** A file in the image is being replaced as a result of a * ::wimlib_add_command without ::WIMLIB_ADD_FLAG_NO_REPLACE specified. * @p info will point to ::wimlib_progress_info.replace. This is only * received when ::WIMLIB_ADD_FLAG_VERBOSE is also specified in the add * command. */ WIMLIB_PROGRESS_MSG_REPLACE_FILE_IN_WIM = 23, /** An image is being extracted with ::WIMLIB_EXTRACT_FLAG_WIMBOOT, and * a file is being extracted normally (not as a "WIMBoot pointer file") * due to it matching a pattern in the [PrepopulateList] section * of the configuration file * /Windows/System32/WimBootCompress.ini in the WIM image. @p * info will point to ::wimlib_progress_info.wimboot_exclude. */ WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE = 24, /** Starting to unmount an image. @p info will point to * ::wimlib_progress_info.unmount. */ WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN = 25, /** wimlib has used a file's data for the last time (including all data * streams, if it has multiple). @p info will point to * ::wimlib_progress_info.done_with_file. This message is only received * if ::WIMLIB_WRITE_FLAG_SEND_DONE_WITH_FILE_MESSAGES was provided. */ WIMLIB_PROGRESS_MSG_DONE_WITH_FILE = 26, /** wimlib_verify_wim() is starting to verify the metadata for an image. * @p info will point to ::wimlib_progress_info.verify_image. */ WIMLIB_PROGRESS_MSG_BEGIN_VERIFY_IMAGE = 27, /** wimlib_verify_wim() has finished verifying the metadata for an * image. @p info will point to ::wimlib_progress_info.verify_image. */ WIMLIB_PROGRESS_MSG_END_VERIFY_IMAGE = 28, /** wimlib_verify_wim() is verifying file data integrity. @p info will * point to ::wimlib_progress_info.verify_streams. */ WIMLIB_PROGRESS_MSG_VERIFY_STREAMS = 29, /** * The progress function is being asked whether a file should be * excluded from capture or not. @p info will point to * ::wimlib_progress_info.test_file_exclusion. This is a bidirectional * message that allows the progress function to set a flag if the file * should be excluded. * * This message is only received if the flag * ::WIMLIB_ADD_FLAG_TEST_FILE_EXCLUSION is used. This method for file * exclusions is independent of the "capture configuration file" * mechanism. */ WIMLIB_PROGRESS_MSG_TEST_FILE_EXCLUSION = 30, /** * An error has occurred and the progress function is being asked * whether to ignore the error or not. @p info will point to * ::wimlib_progress_info.handle_error. This is a bidirectional * message. * * This message provides a limited capability for applications to * recover from "unexpected" errors (i.e. those with no in-library * handling policy) arising from the underlying operating system. * Normally, any such error will cause the library to abort the current * operation. By implementing a handler for this message, the * application can instead choose to ignore a given error. * * Currently, only the following types of errors will result in this * progress message being sent: * * - Directory tree scan errors, e.g. from wimlib_add_image() * - Most extraction errors; currently restricted to the Windows * build of the library only. */ WIMLIB_PROGRESS_MSG_HANDLE_ERROR = 31, }; /** Valid return values from user-provided progress functions * (::wimlib_progress_func_t). * * (Note: if an invalid value is returned, ::WIMLIB_ERR_UNKNOWN_PROGRESS_STATUS * will be issued.) */ enum wimlib_progress_status { /** The operation should be continued. This is the normal return value. */ WIMLIB_PROGRESS_STATUS_CONTINUE = 0, /** The operation should be aborted. This will cause the current * operation to fail with ::WIMLIB_ERR_ABORTED_BY_PROGRESS. */ WIMLIB_PROGRESS_STATUS_ABORT = 1, }; /** * A pointer to this union is passed to the user-supplied * ::wimlib_progress_func_t progress function. One (or none) of the structures * contained in this union will be applicable for the operation * (::wimlib_progress_msg) indicated in the first argument to the progress * function. */ union wimlib_progress_info { /** Valid on the message ::WIMLIB_PROGRESS_MSG_WRITE_STREAMS. This is * the primary message for tracking the progress of writing a WIM file. */ struct wimlib_progress_info_write_streams { /** An upper bound on the number of bytes of file data that will * be written. This number is the uncompressed size; the actual * size may be lower due to compression. In addition, this * number may decrease over time as duplicated file data is * discovered. */ uint64_t total_bytes; /** An upper bound on the number of distinct file data "blobs" * that will be written. This will often be similar to the * "number of files", but for several reasons (hard links, named * data streams, empty files, etc.) it can be different. In * addition, this number may decrease over time as duplicated * file data is discovered. */ uint64_t total_streams; /** The number of bytes of file data that have been written so * far. This starts at 0 and ends at @p total_bytes. This * number is the uncompressed size; the actual size may be lower * due to compression. */ uint64_t completed_bytes; /** The number of distinct file data "blobs" that have been * written so far. This starts at 0 and ends at @p * total_streams. */ uint64_t completed_streams; /** The number of threads being used for data compression; or, * if no compression is being performed, this will be 1. */ uint32_t num_threads; /** The compression type being used, as one of the * ::wimlib_compression_type constants. */ int32_t compression_type; /** The number of on-disk WIM files from which file data is * being exported into the output WIM file. This can be 0, 1, * or more than 1, depending on the situation. */ uint32_t total_parts; /** This is currently broken and will always be 0. */ uint32_t completed_parts; } write_streams; /** Valid on messages ::WIMLIB_PROGRESS_MSG_SCAN_BEGIN, * ::WIMLIB_PROGRESS_MSG_SCAN_DENTRY, and * ::WIMLIB_PROGRESS_MSG_SCAN_END. */ struct wimlib_progress_info_scan { /** Top-level directory being scanned; or, when capturing an NTFS * volume with ::WIMLIB_ADD_FLAG_NTFS, this is instead the path * to the file or block device that contains the NTFS volume * being scanned. */ const wimlib_tchar *source; /** Path to the file (or directory) that has been scanned, valid * on ::WIMLIB_PROGRESS_MSG_SCAN_DENTRY. When capturing an NTFS * volume with ::WIMLIB_ADD_FLAG_NTFS, this path will be * relative to the root of the NTFS volume. */ const wimlib_tchar *cur_path; /** Dentry scan status, valid on * ::WIMLIB_PROGRESS_MSG_SCAN_DENTRY. */ enum { /** File looks okay and will be captured. */ WIMLIB_SCAN_DENTRY_OK = 0, /** File is being excluded from capture due to the * capture configuration. */ WIMLIB_SCAN_DENTRY_EXCLUDED = 1, /** File is being excluded from capture due to being of * an unsupported type. */ WIMLIB_SCAN_DENTRY_UNSUPPORTED = 2, /** The file is an absolute symbolic link or junction * that points into the capture directory, and * reparse-point fixups are enabled, so its target is * being adjusted. (Reparse point fixups can be * disabled with the flag ::WIMLIB_ADD_FLAG_NORPFIX.) */ WIMLIB_SCAN_DENTRY_FIXED_SYMLINK = 3, /** Reparse-point fixups are enabled, but the file is an * absolute symbolic link or junction that does * not point into the capture directory, so its * target is not being adjusted. */ WIMLIB_SCAN_DENTRY_NOT_FIXED_SYMLINK = 4, } status; union { /** Target path in the image. Only valid on messages * ::WIMLIB_PROGRESS_MSG_SCAN_BEGIN and * ::WIMLIB_PROGRESS_MSG_SCAN_END. */ const wimlib_tchar *wim_target_path; /** For ::WIMLIB_PROGRESS_MSG_SCAN_DENTRY and a status * of @p WIMLIB_SCAN_DENTRY_FIXED_SYMLINK or @p * WIMLIB_SCAN_DENTRY_NOT_FIXED_SYMLINK, this is the * target of the absolute symbolic link or junction. */ const wimlib_tchar *symlink_target; }; /** The number of directories scanned so far, not counting * excluded/unsupported files. */ uint64_t num_dirs_scanned; /** The number of non-directories scanned so far, not counting * excluded/unsupported files. */ uint64_t num_nondirs_scanned; /** The number of bytes of file data detected so far, not * counting excluded/unsupported files. */ uint64_t num_bytes_scanned; } scan; /** Valid on messages * ::WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN, * ::WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN, * ::WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN, * ::WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE, * ::WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS, * ::WIMLIB_PROGRESS_MSG_EXTRACT_METADATA, * ::WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END, and * ::WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END. * * Note: most of the time of an extraction operation will be spent * extracting file data, and the application will receive * ::WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS during this time. Using @p * completed_bytes and @p total_bytes, the application can calculate a * percentage complete. However, there is no way for applications to * know which file is currently being extracted. This is by design * because the best way to complete the extraction operation is not * necessarily file-by-file. */ struct wimlib_progress_info_extract { /** The 1-based index of the image from which files are being * extracted. */ uint32_t image; /** Extraction flags being used. */ uint32_t extract_flags; /** If the ::WIMStruct from which the extraction being performed * has a backing file, then this is an absolute path to that * backing file. Otherwise, this is @c NULL. */ const wimlib_tchar *wimfile_name; /** Name of the image from which files are being extracted, or * the empty string if the image is unnamed. */ const wimlib_tchar *image_name; /** Path to the directory or NTFS volume to which the files are * being extracted. */ const wimlib_tchar *target; /** Reserved. */ const wimlib_tchar *reserved; /** The number of bytes of file data that will be extracted. */ uint64_t total_bytes; /** The number of bytes of file data that have been extracted so * far. This starts at 0 and ends at @p total_bytes. */ uint64_t completed_bytes; /** The number of file streams that will be extracted. This * will often be similar to the "number of files", but for * several reasons (hard links, named data streams, empty files, * etc.) it can be different. */ uint64_t total_streams; /** The number of file streams that have been extracted so far. * This starts at 0 and ends at @p total_streams. */ uint64_t completed_streams; /** Currently only used for * ::WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN. */ uint32_t part_number; /** Currently only used for * ::WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN. */ uint32_t total_parts; /** Currently only used for * ::WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN. */ uint8_t guid[WIMLIB_GUID_LEN]; /** For ::WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE and * ::WIMLIB_PROGRESS_MSG_EXTRACT_METADATA messages, this is the * number of files that have been processed so far. Once the * corresponding phase of extraction is complete, this value * will be equal to @c end_file_count. */ uint64_t current_file_count; /** For ::WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE and * ::WIMLIB_PROGRESS_MSG_EXTRACT_METADATA messages, this is * total number of files that will be processed. * * This number is provided for informational purposes only, e.g. * for a progress bar. This number will not necessarily be * equal to the number of files actually being extracted. This * is because extraction backends are free to implement an * extraction algorithm that might be more efficient than * processing every file in the "extract file structure" and * "extract file metadata" phases. For example, the current * implementation of the UNIX extraction backend will create * files on-demand during the "extract file data" phase. * Therefore, when using that particular extraction backend, @p * end_file_count will only include directories and empty files. */ uint64_t end_file_count; } extract; /** Valid on messages ::WIMLIB_PROGRESS_MSG_RENAME. */ struct wimlib_progress_info_rename { /** Name of the temporary file that the WIM was written to. */ const wimlib_tchar *from; /** Name of the original WIM file to which the temporary file is * being renamed. */ const wimlib_tchar *to; } rename; /** Valid on messages ::WIMLIB_PROGRESS_MSG_UPDATE_BEGIN_COMMAND and * ::WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND. */ struct wimlib_progress_info_update { /** Pointer to the update command that will be executed or has * just been executed. */ const struct wimlib_update_command *command; /** Number of update commands that have been completed so far. */ size_t completed_commands; /** Number of update commands that are being executed as part of * this call to wimlib_update_image(). */ size_t total_commands; } update; /** Valid on messages ::WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY and * ::WIMLIB_PROGRESS_MSG_CALC_INTEGRITY. */ struct wimlib_progress_info_integrity { /** The number of bytes in the WIM file that are covered by * integrity checks. */ uint64_t total_bytes; /** The number of bytes that have been checksummed so far. This * starts at 0 and ends at @p total_bytes. */ uint64_t completed_bytes; /** The number of individually checksummed "chunks" the * integrity-checked region is divided into. */ uint32_t total_chunks; /** The number of chunks that have been checksummed so far. * This starts at 0 and ends at @p total_chunks. */ uint32_t completed_chunks; /** The size of each individually checksummed "chunk" in the * integrity-checked region. */ uint32_t chunk_size; /** For ::WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY messages, this is * the path to the WIM file being checked. */ const wimlib_tchar *filename; } integrity; /** Valid on messages ::WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART and * ::WIMLIB_PROGRESS_MSG_SPLIT_END_PART. */ struct wimlib_progress_info_split { /** Total size of the original WIM's file and metadata resources * (compressed). */ uint64_t total_bytes; /** Number of bytes of file and metadata resources that have * been copied out of the original WIM so far. Will be 0 * initially, and equal to @p total_bytes at the end. */ uint64_t completed_bytes; /** Number of the split WIM part that is about to be started * (::WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART) or has just been * finished (::WIMLIB_PROGRESS_MSG_SPLIT_END_PART). */ unsigned cur_part_number; /** Total number of split WIM parts that are being written. */ unsigned total_parts; /** Name of the split WIM part that is about to be started * (::WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART) or has just been * finished (::WIMLIB_PROGRESS_MSG_SPLIT_END_PART). Since * wimlib v1.7.0, the library user may change this when * receiving ::WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART in order to * cause the next split WIM part to be written to a different * location. */ wimlib_tchar *part_name; } split; /** Valid on messages ::WIMLIB_PROGRESS_MSG_REPLACE_FILE_IN_WIM */ struct wimlib_progress_info_replace { /** Path to the file in the image that is being replaced */ const wimlib_tchar *path_in_wim; } replace; /** Valid on messages ::WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE */ struct wimlib_progress_info_wimboot_exclude { /** Path to the file in the image */ const wimlib_tchar *path_in_wim; /** Path to which the file is being extracted */ const wimlib_tchar *extraction_path; } wimboot_exclude; /** Valid on messages ::WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN. */ struct wimlib_progress_info_unmount { /** Path to directory being unmounted */ const wimlib_tchar *mountpoint; /** Path to WIM file being unmounted */ const wimlib_tchar *mounted_wim; /** 1-based index of image being unmounted. */ uint32_t mounted_image; /** Flags that were passed to wimlib_mount_image() when the * mountpoint was set up. */ uint32_t mount_flags; /** Flags passed to wimlib_unmount_image(). */ uint32_t unmount_flags; } unmount; /** Valid on messages ::WIMLIB_PROGRESS_MSG_DONE_WITH_FILE. */ struct wimlib_progress_info_done_with_file { /** * Path to the file whose data has been written to the WIM file, * or is currently being asynchronously compressed in memory, * and therefore is no longer needed by wimlib. * * WARNING: The file data will not actually be accessible in the * WIM file until the WIM file has been completely written. * Ordinarily you should not treat this message as a * green light to go ahead and delete the specified file, since * that would result in data loss if the WIM file cannot be * successfully created for any reason. * * If a file has multiple names (hard links), * ::WIMLIB_PROGRESS_MSG_DONE_WITH_FILE will only be received * for one name. Also, this message will not be received for * empty files or reparse points (or symbolic links), unless * they have nonempty named data streams. */ const wimlib_tchar *path_to_file; } done_with_file; /** Valid on messages ::WIMLIB_PROGRESS_MSG_BEGIN_VERIFY_IMAGE and * ::WIMLIB_PROGRESS_MSG_END_VERIFY_IMAGE. */ struct wimlib_progress_info_verify_image { const wimlib_tchar *wimfile; uint32_t total_images; uint32_t current_image; } verify_image; /** Valid on messages ::WIMLIB_PROGRESS_MSG_VERIFY_STREAMS. */ struct wimlib_progress_info_verify_streams { const wimlib_tchar *wimfile; uint64_t total_streams; uint64_t total_bytes; uint64_t completed_streams; uint64_t completed_bytes; } verify_streams; /** Valid on messages ::WIMLIB_PROGRESS_MSG_TEST_FILE_EXCLUSION. */ struct wimlib_progress_info_test_file_exclusion { /** * Path to the file for which exclusion is being tested. * * UNIX capture mode: The path will be a standard relative or * absolute UNIX filesystem path. * * NTFS-3G capture mode: The path will be given relative to the * root of the NTFS volume, with a leading slash. * * Windows capture mode: The path will be a Win32 namespace * path to the file. */ const wimlib_tchar *path; /** * Indicates whether the file or directory will be excluded from * capture or not. This will be false by default. The * progress function can set this to true if it decides * that the file needs to be excluded. */ bool will_exclude; } test_file_exclusion; /** Valid on messages ::WIMLIB_PROGRESS_MSG_HANDLE_ERROR. */ struct wimlib_progress_info_handle_error { /** Path to the file for which the error occurred, or NULL if * not relevant. */ const wimlib_tchar *path; /** The wimlib error code associated with the error. */ int error_code; /** * Indicates whether the error will be ignored or not. This * will be false by default; the progress function may * set it to true. */ bool will_ignore; } handle_error; }; /** * A user-supplied function that will be called periodically during certain WIM * operations. * * The first argument will be the type of operation that is being performed or * is about to be started or has been completed. * * The second argument will be a pointer to one of a number of structures * depending on the first argument. It may be @c NULL for some message types. * Note that although this argument is not @c const, users should not modify it * except in explicitly documented cases. * * The third argument will be a user-supplied value that was provided when * registering or specifying the progress function. * * This function must return one of the ::wimlib_progress_status values. By * default, you should return ::WIMLIB_PROGRESS_STATUS_CONTINUE (0). */ typedef enum wimlib_progress_status (*wimlib_progress_func_t)(enum wimlib_progress_msg msg_type, union wimlib_progress_info *info, void *progctx); /** @} */ /** @addtogroup G_modifying_wims * @{ */ /** An array of these structures is passed to wimlib_add_image_multisource() to * specify the sources from which to create a WIM image. */ struct wimlib_capture_source { /** Absolute or relative path to a file or directory on the external * filesystem to be included in the image. */ wimlib_tchar *fs_source_path; /** Destination path in the image. To specify the root directory of the * image, use ::WIMLIB_WIM_ROOT_PATH. */ wimlib_tchar *wim_target_path; /** Reserved; set to 0. */ long reserved; }; /** Set or unset the "readonly" WIM header flag (WIM_HDR_FLAG_READONLY in * Microsoft's documentation), based on the ::wimlib_wim_info.is_marked_readonly * member of the @p info parameter. This is distinct from basic file * permissions; this flag can be set on a WIM file that is physically writable. * * wimlib disallows modifying on-disk WIM files with the readonly flag set. * However, wimlib_overwrite() with ::WIMLIB_WRITE_FLAG_IGNORE_READONLY_FLAG * will override this --- and in fact, this is necessary to set the readonly * flag persistently on an existing WIM file. */ #define WIMLIB_CHANGE_READONLY_FLAG 0x00000001 /** Set the GUID (globally unique identifier) of the WIM file to the value * specified in ::wimlib_wim_info.guid of the @p info parameter. */ #define WIMLIB_CHANGE_GUID 0x00000002 /** Change the bootable image of the WIM to the value specified in * ::wimlib_wim_info.boot_index of the @p info parameter. */ #define WIMLIB_CHANGE_BOOT_INDEX 0x00000004 /** Change the WIM_HDR_FLAG_RP_FIX flag of the WIM file to the value * specified in ::wimlib_wim_info.has_rpfix of the @p info parameter. This flag * generally indicates whether an image in the WIM has been captured with * reparse-point fixups enabled. wimlib also treats this flag as specifying * whether to do reparse-point fixups by default when capturing or applying WIM * images. */ #define WIMLIB_CHANGE_RPFIX_FLAG 0x00000008 /** @} */ /** @addtogroup G_wim_information */ /** @{ */ /** * General information about a WIM file. * * This info can also be requested for a ::WIMStruct that does not have a * backing file. In this case, fields that only make sense given a backing file * are set to default values. */ struct wimlib_wim_info { /** The globally unique identifier for this WIM. (Note: all parts of a * split WIM normally have identical GUIDs.) */ uint8_t guid[WIMLIB_GUID_LEN]; /** The number of images in this WIM file. */ uint32_t image_count; /** The 1-based index of the bootable image in this WIM file, or 0 if no * image is bootable. */ uint32_t boot_index; /** The version of the WIM file format used in this WIM file. */ uint32_t wim_version; /** The default compression chunk size of resources in this WIM file. */ uint32_t chunk_size; /** For split WIMs, the 1-based index of this part within the split WIM; * otherwise 1. */ uint16_t part_number; /** For split WIMs, the total number of parts in the split WIM; * otherwise 1. */ uint16_t total_parts; /** The default compression type of resources in this WIM file, as one * of the ::wimlib_compression_type constants. */ int32_t compression_type; /** The size of this WIM file in bytes, excluding the XML data and * integrity table. */ uint64_t total_bytes; /** 1 iff this WIM file has an integrity table. */ uint32_t has_integrity_table : 1; /** 1 iff this info struct is for a ::WIMStruct that has a backing file. */ uint32_t opened_from_file : 1; /** 1 iff this WIM file is considered readonly for any reason (e.g. the * "readonly" header flag is set, or this is part of a split WIM, or * filesystem permissions deny writing) */ uint32_t is_readonly : 1; /** 1 iff the "reparse point fix" flag is set in this WIM's header */ uint32_t has_rpfix : 1; /** 1 iff the "readonly" flag is set in this WIM's header */ uint32_t is_marked_readonly : 1; /** 1 iff the "spanned" flag is set in this WIM's header */ uint32_t spanned : 1; /** 1 iff the "write in progress" flag is set in this WIM's header */ uint32_t write_in_progress : 1; /** 1 iff the "metadata only" flag is set in this WIM's header */ uint32_t metadata_only : 1; /** 1 iff the "resource only" flag is set in this WIM's header */ uint32_t resource_only : 1; /** 1 iff this WIM file is pipable (see ::WIMLIB_WRITE_FLAG_PIPABLE). */ uint32_t pipable : 1; uint32_t reserved_flags : 22; uint32_t reserved[9]; }; /** * Information about a "blob", which is a fixed length sequence of binary data. * Each nonempty stream of each file in a WIM image is associated with a blob. * Blobs are deduplicated within a WIM file. * * TODO: this struct needs to be renamed, and perhaps made into a union since * there are several cases. I'll try to list them below: * * 1. The blob is "missing", meaning that it is referenced by hash but not * actually present in the WIM file. In this case we only know the * sha1_hash. This case can only occur with wimlib_iterate_dir_tree(), never * wimlib_iterate_lookup_table(). * * 2. Otherwise we know the sha1_hash, the uncompressed_size, the * reference_count, and the is_metadata flag. In addition: * * A. If the blob is located in a non-solid WIM resource, then we also know * the compressed_size and offset. * * B. If the blob is located in a solid WIM resource, then we also know the * offset, raw_resource_offset_in_wim, raw_resource_compressed_size, and * raw_resource_uncompressed_size. But the "offset" is actually the * offset in the uncompressed solid resource rather than the offset from * the beginning of the WIM file. * * C. If the blob is *not* located in any type of WIM resource, then we don't * know any additional information. * * Unknown or irrelevant fields are left zeroed. */ struct wimlib_resource_entry { /** If this blob is not missing, then this is the uncompressed size of * this blob in bytes. */ uint64_t uncompressed_size; /** If this blob is located in a non-solid WIM resource, then this is * the compressed size of that resource. */ uint64_t compressed_size; /** If this blob is located in a non-solid WIM resource, then this is * the offset of that resource within the WIM file containing it. If * this blob is located in a solid WIM resource, then this is the offset * of this blob within that solid resource when uncompressed. */ uint64_t offset; /** The SHA-1 message digest of the blob's uncompressed contents. */ uint8_t sha1_hash[20]; /** If this blob is located in a WIM resource, then this is the part * number of the WIM file containing it. */ uint32_t part_number; /** If this blob is not missing, then this is the number of times this * blob is referenced over all images in the WIM. This number is not * guaranteed to be correct. */ uint32_t reference_count; /** 1 iff this blob is located in a non-solid compressed WIM resource. */ uint32_t is_compressed : 1; /** 1 iff this blob contains the metadata for an image. */ uint32_t is_metadata : 1; uint32_t is_free : 1; uint32_t is_spanned : 1; /** 1 iff a blob with this hash was not found in the blob lookup table * of the ::WIMStruct. This normally implies a missing call to * wimlib_reference_resource_files() or wimlib_reference_resources(). */ uint32_t is_missing : 1; /** 1 iff this blob is located in a solid resource. */ uint32_t packed : 1; uint32_t reserved_flags : 26; /** If this blob is located in a solid WIM resource, then this is the * offset of that solid resource within the WIM file containing it. */ uint64_t raw_resource_offset_in_wim; /** If this blob is located in a solid WIM resource, then this is the * compressed size of that solid resource. */ uint64_t raw_resource_compressed_size; /** If this blob is located in a solid WIM resource, then this is the * uncompressed size of that solid resource. */ uint64_t raw_resource_uncompressed_size; uint64_t reserved[1]; }; /** * Information about a stream of a particular file in the WIM. * * Normally, only WIM images captured from NTFS filesystems will have multiple * streams per file. In practice, this is a rarely used feature of the * filesystem. * * TODO: the library now explicitly tracks stream types, which allows it to have * multiple unnamed streams (e.g. both a reparse point stream and unnamed data * stream). However, this isn't yet exposed by wimlib_iterate_dir_tree(). */ struct wimlib_stream_entry { /** Name of the stream, or NULL if the stream is unnamed. */ const wimlib_tchar *stream_name; /** Info about this stream's data, such as its hash and size if known.*/ struct wimlib_resource_entry resource; uint64_t reserved[4]; }; /** * Since wimlib v1.9.1: an object ID, which is an extra piece of metadata that * may be associated with a file on NTFS filesystems. See: * https://msdn.microsoft.com/en-us/library/windows/desktop/aa363997(v=vs.85).aspx */ struct wimlib_object_id { uint8_t object_id[WIMLIB_GUID_LEN]; uint8_t birth_volume_id[WIMLIB_GUID_LEN]; uint8_t birth_object_id[WIMLIB_GUID_LEN]; uint8_t domain_id[WIMLIB_GUID_LEN]; }; /** Structure passed to the wimlib_iterate_dir_tree() callback function. * Roughly, the information about a "file" in the WIM image --- but really a * directory entry ("dentry") because hard links are allowed. The * hard_link_group_id field can be used to distinguish actual file inodes. */ struct wimlib_dir_entry { /** Name of the file, or NULL if this file is unnamed. Only the root * directory of an image will be unnamed. */ const wimlib_tchar *filename; /** 8.3 name (or "DOS name", or "short name") of this file; or NULL if * this file has no such name. */ const wimlib_tchar *dos_name; /** Full path to this file within the image. Path separators will be * ::WIMLIB_WIM_PATH_SEPARATOR. */ const wimlib_tchar *full_path; /** Depth of this directory entry, where 0 is the root, 1 is the root's * children, ..., etc. */ size_t depth; /** Pointer to the security descriptor for this file, in Windows * SECURITY_DESCRIPTOR_RELATIVE format, or NULL if this file has no * security descriptor. */ const char *security_descriptor; /** Size of the above security descriptor, in bytes. */ size_t security_descriptor_size; #define WIMLIB_FILE_ATTRIBUTE_READONLY 0x00000001 #define WIMLIB_FILE_ATTRIBUTE_HIDDEN 0x00000002 #define WIMLIB_FILE_ATTRIBUTE_SYSTEM 0x00000004 #define WIMLIB_FILE_ATTRIBUTE_DIRECTORY 0x00000010 #define WIMLIB_FILE_ATTRIBUTE_ARCHIVE 0x00000020 #define WIMLIB_FILE_ATTRIBUTE_DEVICE 0x00000040 #define WIMLIB_FILE_ATTRIBUTE_NORMAL 0x00000080 #define WIMLIB_FILE_ATTRIBUTE_TEMPORARY 0x00000100 #define WIMLIB_FILE_ATTRIBUTE_SPARSE_FILE 0x00000200 #define WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT 0x00000400 #define WIMLIB_FILE_ATTRIBUTE_COMPRESSED 0x00000800 #define WIMLIB_FILE_ATTRIBUTE_OFFLINE 0x00001000 #define WIMLIB_FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000 #define WIMLIB_FILE_ATTRIBUTE_ENCRYPTED 0x00004000 #define WIMLIB_FILE_ATTRIBUTE_VIRTUAL 0x00010000 /** File attributes, such as whether the file is a directory or not. * These are the "standard" Windows FILE_ATTRIBUTE_* values, although in * wimlib.h they are defined as WIMLIB_FILE_ATTRIBUTE_* for convenience * on other platforms. */ uint32_t attributes; #define WIMLIB_REPARSE_TAG_RESERVED_ZERO 0x00000000 #define WIMLIB_REPARSE_TAG_RESERVED_ONE 0x00000001 #define WIMLIB_REPARSE_TAG_MOUNT_POINT 0xA0000003 #define WIMLIB_REPARSE_TAG_HSM 0xC0000004 #define WIMLIB_REPARSE_TAG_HSM2 0x80000006 #define WIMLIB_REPARSE_TAG_DRIVER_EXTENDER 0x80000005 #define WIMLIB_REPARSE_TAG_SIS 0x80000007 #define WIMLIB_REPARSE_TAG_DFS 0x8000000A #define WIMLIB_REPARSE_TAG_DFSR 0x80000012 #define WIMLIB_REPARSE_TAG_FILTER_MANAGER 0x8000000B #define WIMLIB_REPARSE_TAG_WOF 0x80000017 #define WIMLIB_REPARSE_TAG_SYMLINK 0xA000000C /** If the file is a reparse point (FILE_ATTRIBUTE_REPARSE_POINT set in * the attributes), this will give the reparse tag. This tells you * whether the reparse point is a symbolic link, junction point, or some * other, more unusual kind of reparse point. */ uint32_t reparse_tag; /** Number of links to this file's inode (hard links). * * Currently, this will always be 1 for directories. However, it can be * greater than 1 for nondirectory files. */ uint32_t num_links; /** Number of named data streams this file has. Normally 0. */ uint32_t num_named_streams; /** A unique identifier for this file's inode. However, as a special * case, if the inode only has a single link (@p num_links == 1), this * value may be 0. * * Note: if a WIM image is captured from a filesystem, this value is not * guaranteed to be the same as the original number of the inode on the * filesystem. */ uint64_t hard_link_group_id; /** Time this file was created. */ struct wimlib_timespec creation_time; /** Time this file was last written to. */ struct wimlib_timespec last_write_time; /** Time this file was last accessed. */ struct wimlib_timespec last_access_time; /** The UNIX user ID of this file. This is a wimlib extension. * * This field is only valid if @p unix_mode != 0. */ uint32_t unix_uid; /** The UNIX group ID of this file. This is a wimlib extension. * * This field is only valid if @p unix_mode != 0. */ uint32_t unix_gid; /** The UNIX mode of this file. This is a wimlib extension. * * If this field is 0, then @p unix_uid, @p unix_gid, @p unix_mode, and * @p unix_rdev are all unknown (fields are not present in the WIM * image). */ uint32_t unix_mode; /** The UNIX device ID (major and minor number) of this file. This is a * wimlib extension. * * This field is only valid if @p unix_mode != 0. */ uint32_t unix_rdev; /* The object ID of this file, if any. Only valid if * object_id.object_id is not all zeroes. */ struct wimlib_object_id object_id; /** High 32 bits of the seconds portion of the creation timestamp, * filled in if @p wimlib_timespec.tv_sec is only 32-bit. */ int32_t creation_time_high; /** High 32 bits of the seconds portion of the last write timestamp, * filled in if @p wimlib_timespec.tv_sec is only 32-bit. */ int32_t last_write_time_high; /** High 32 bits of the seconds portion of the last access timestamp, * filled in if @p wimlib_timespec.tv_sec is only 32-bit. */ int32_t last_access_time_high; int32_t reserved2; uint64_t reserved[4]; /** * Variable-length array of streams that make up this file. * * The first entry will always exist and will correspond to the unnamed * data stream (default file contents), so it will have stream_name * == NULL. Alternatively, for reparse point files, the first entry * will correspond to the reparse data stream. Alternatively, for * encrypted files, the first entry will correspond to the encrypted * data. * * Then, following the first entry, there be @p num_named_streams * additional entries that specify the named data streams, if any, each * of which will have stream_name != NULL. */ struct wimlib_stream_entry streams[]; }; /** * Type of a callback function to wimlib_iterate_dir_tree(). Must return 0 on * success. */ typedef int (*wimlib_iterate_dir_tree_callback_t)(const struct wimlib_dir_entry *dentry, void *user_ctx); /** * Type of a callback function to wimlib_iterate_lookup_table(). Must return 0 * on success. */ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resource_entry *resource, void *user_ctx); /** For wimlib_iterate_dir_tree(): Iterate recursively on children rather than * just on the specified path. */ #define WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE 0x00000001 /** For wimlib_iterate_dir_tree(): Don't iterate on the file or directory * itself; only its children (in the case of a non-empty directory) */ #define WIMLIB_ITERATE_DIR_TREE_FLAG_CHILDREN 0x00000002 /** Return ::WIMLIB_ERR_RESOURCE_NOT_FOUND if any file data blobs needed to fill * in the ::wimlib_resource_entry's for the iteration cannot be found in the * blob lookup table of the ::WIMStruct. The default behavior without this flag * is to fill in the @ref wimlib_resource_entry::sha1_hash "sha1_hash" and set * the @ref wimlib_resource_entry::is_missing "is_missing" flag. */ #define WIMLIB_ITERATE_DIR_TREE_FLAG_RESOURCES_NEEDED 0x00000004 /** @} */ /** @addtogroup G_modifying_wims * @{ */ /** UNIX-like systems only: Directly capture an NTFS volume rather than a * generic directory. This requires that wimlib was compiled with support for * libntfs-3g. * * This flag cannot be combined with ::WIMLIB_ADD_FLAG_DEREFERENCE or * ::WIMLIB_ADD_FLAG_UNIX_DATA. * * Do not use this flag on Windows, where wimlib already supports all * Windows-native filesystems, including NTFS, through the Windows APIs. */ #define WIMLIB_ADD_FLAG_NTFS 0x00000001 /** Follow symbolic links when scanning the directory tree. Currently only * supported on UNIX-like systems. */ #define WIMLIB_ADD_FLAG_DEREFERENCE 0x00000002 /** Call the progress function with the message * ::WIMLIB_PROGRESS_MSG_SCAN_DENTRY when each directory or file has been * scanned. */ #define WIMLIB_ADD_FLAG_VERBOSE 0x00000004 /** Mark the image being added as the bootable image of the WIM. This flag is * valid only for wimlib_add_image() and wimlib_add_image_multisource(). * * Note that you can also change the bootable image of a WIM using * wimlib_set_wim_info(). * * Note: ::WIMLIB_ADD_FLAG_BOOT does something different from, and independent * from, ::WIMLIB_ADD_FLAG_WIMBOOT. */ #define WIMLIB_ADD_FLAG_BOOT 0x00000008 /** UNIX-like systems only: Store the UNIX owner, group, mode, and device ID * (major and minor number) of each file. In addition, capture special files * such as device nodes and FIFOs. Since wimlib v1.11.0, on Linux also capture * extended attributes. See the documentation for the --unix-data option * to wimcapture for more information. */ #define WIMLIB_ADD_FLAG_UNIX_DATA 0x00000010 /** Do not capture security descriptors. Only has an effect in NTFS-3G capture * mode, or in Windows native builds. */ #define WIMLIB_ADD_FLAG_NO_ACLS 0x00000020 /** Fail immediately if the full security descriptor of any file or directory * cannot be accessed. Only has an effect in Windows native builds. The * default behavior without this flag is to first try omitting the SACL from the * security descriptor, then to try omitting the security descriptor entirely. */ #define WIMLIB_ADD_FLAG_STRICT_ACLS 0x00000040 /** Call the progress function with the message * ::WIMLIB_PROGRESS_MSG_SCAN_DENTRY when a directory or file is excluded from * capture. This is a subset of the messages provided by * ::WIMLIB_ADD_FLAG_VERBOSE. */ #define WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE 0x00000080 /** Reparse-point fixups: Modify absolute symbolic links (and junctions, in the * case of Windows) that point inside the directory being captured to instead be * absolute relative to the directory being captured. * * Without this flag, the default is to do reparse-point fixups if * WIM_HDR_FLAG_RP_FIX is set in the WIM header or if this is the first * image being added. */ #define WIMLIB_ADD_FLAG_RPFIX 0x00000100 /** Don't do reparse point fixups. See ::WIMLIB_ADD_FLAG_RPFIX. */ #define WIMLIB_ADD_FLAG_NORPFIX 0x00000200 /** Do not automatically exclude unsupported files or directories from capture, * such as encrypted files in NTFS-3G capture mode, or device files and FIFOs on * UNIX-like systems when not also using ::WIMLIB_ADD_FLAG_UNIX_DATA. Instead, * fail with ::WIMLIB_ERR_UNSUPPORTED_FILE when such a file is encountered. */ #define WIMLIB_ADD_FLAG_NO_UNSUPPORTED_EXCLUDE 0x00000400 /** * Automatically select a capture configuration appropriate for capturing * filesystems containing Windows operating systems. For example, * /pagefile.sys and "/System Volume Information" will be * excluded. * * When this flag is specified, the corresponding @p config parameter (for * wimlib_add_image()) or member (for wimlib_update_image()) must be @c NULL. * Otherwise, ::WIMLIB_ERR_INVALID_PARAM will be returned. * * Note that the default behavior--- that is, when neither * ::WIMLIB_ADD_FLAG_WINCONFIG nor ::WIMLIB_ADD_FLAG_WIMBOOT is specified and @p * config is @c NULL--- is to use no capture configuration, meaning that no * files are excluded from capture. */ #define WIMLIB_ADD_FLAG_WINCONFIG 0x00000800 /** * Capture image as "WIMBoot compatible". In addition, if no capture * configuration file is explicitly specified use the capture configuration file * $SOURCE/Windows/System32/WimBootCompress.ini if it exists, where * $SOURCE is the directory being captured; or, if a capture * configuration file is explicitly specified, use it and also place it at * /Windows/System32/WimBootCompress.ini in the WIM image. * * This flag does not, by itself, change the compression type or chunk size. * Before writing the WIM file, you may wish to set the compression format to * be the same as that used by WIMGAPI and DISM: * * \code * wimlib_set_output_compression_type(wim, WIMLIB_COMPRESSION_TYPE_XPRESS); * wimlib_set_output_chunk_size(wim, 4096); * \endcode * * However, "WIMBoot" also works with other XPRESS chunk sizes as well as LZX * with 32768 byte chunks. * * Note: ::WIMLIB_ADD_FLAG_WIMBOOT does something different from, and * independent from, ::WIMLIB_ADD_FLAG_BOOT. * * Since wimlib v1.8.3, ::WIMLIB_ADD_FLAG_WIMBOOT also causes offline WIM-backed * files to be added as the "real" files rather than as their reparse points, * provided that their data is already present in the WIM. This feature can be * useful when updating a backing WIM file in an "offline" state. */ #define WIMLIB_ADD_FLAG_WIMBOOT 0x00001000 /** * If the add command involves adding a non-directory file to a location at * which there already exists a nondirectory file in the image, issue * ::WIMLIB_ERR_INVALID_OVERLAY instead of replacing the file. This was the * default behavior before wimlib v1.7.0. */ #define WIMLIB_ADD_FLAG_NO_REPLACE 0x00002000 /** * Send ::WIMLIB_PROGRESS_MSG_TEST_FILE_EXCLUSION messages to the progress * function. * * Note: This method for file exclusions is independent from the capture * configuration file mechanism. */ #define WIMLIB_ADD_FLAG_TEST_FILE_EXCLUSION 0x00004000 /** * Since wimlib v1.9.0: create a temporary filesystem snapshot of the source * directory and add the files from it. Currently, this option is only * supported on Windows, where it uses the Volume Shadow Copy Service (VSS). * Using this option, you can create a consistent backup of the system volume of * a running Windows system without running into problems with locked files. * For the VSS snapshot to be successfully created, your application must be run * as an Administrator, and it cannot be run in WoW64 mode (i.e. if Windows is * 64-bit, then your application must be 64-bit as well). */ #define WIMLIB_ADD_FLAG_SNAPSHOT 0x00008000 /** * Since wimlib v1.9.0: permit the library to discard file paths after the * initial scan. If the application won't use * ::WIMLIB_WRITE_FLAG_SEND_DONE_WITH_FILE_MESSAGES while writing the WIM * archive, this flag can be used to allow the library to enable optimizations * such as opening files by inode number rather than by path. Currently this * only makes a difference on Windows. */ #define WIMLIB_ADD_FLAG_FILE_PATHS_UNNEEDED 0x00010000 /** @} */ /** @addtogroup G_modifying_wims * @{ */ /** Do not issue an error if the path to delete does not exist. */ #define WIMLIB_DELETE_FLAG_FORCE 0x00000001 /** Delete the file or directory tree recursively; if not specified, an error is * issued if the path to delete is a directory. */ #define WIMLIB_DELETE_FLAG_RECURSIVE 0x00000002 /** @} */ /** @addtogroup G_modifying_wims * @{ */ /** * If a single image is being exported, mark it bootable in the destination WIM. * Alternatively, if ::WIMLIB_ALL_IMAGES is specified as the image to export, * the image in the source WIM (if any) that is marked as bootable is also * marked as bootable in the destination WIM. */ #define WIMLIB_EXPORT_FLAG_BOOT 0x00000001 /** Give the exported image(s) no names. Avoids problems with image name * collisions. */ #define WIMLIB_EXPORT_FLAG_NO_NAMES 0x00000002 /** Give the exported image(s) no descriptions. */ #define WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS 0x00000004 /** This advises the library that the program is finished with the source * WIMStruct and will not attempt to access it after the call to * wimlib_export_image(), with the exception of the call to wimlib_free(). */ #define WIMLIB_EXPORT_FLAG_GIFT 0x00000008 /** * Mark each exported image as WIMBoot-compatible. * * Note: by itself, this does change the destination WIM's compression type, nor * does it add the file @c \\Windows\\System32\\WimBootCompress.ini in the WIM * image. Before writing the destination WIM, it's recommended to do something * like: * * \code * wimlib_set_output_compression_type(wim, WIMLIB_COMPRESSION_TYPE_XPRESS); * wimlib_set_output_chunk_size(wim, 4096); * wimlib_add_tree(wim, image, L"myconfig.ini", * L"\\Windows\\System32\\WimBootCompress.ini", 0); * \endcode */ #define WIMLIB_EXPORT_FLAG_WIMBOOT 0x00000010 /** @} */ /** @addtogroup G_extracting_wims * @{ */ /** Extract the image directly to an NTFS volume rather than a generic directory. * This mode is only available if wimlib was compiled with libntfs-3g support; * if not, ::WIMLIB_ERR_UNSUPPORTED will be returned. In this mode, the * extraction target will be interpreted as the path to an NTFS volume image (as * a regular file or block device) rather than a directory. It will be opened * using libntfs-3g, and the image will be extracted to the NTFS filesystem's * root directory. Note: this flag cannot be used when wimlib_extract_image() * is called with ::WIMLIB_ALL_IMAGES as the @p image, nor can it be used with * wimlib_extract_paths() when passed multiple paths. */ #define WIMLIB_EXTRACT_FLAG_NTFS 0x00000001 /** UNIX-like systems only: Extract UNIX-specific metadata captured with * ::WIMLIB_ADD_FLAG_UNIX_DATA. */ #define WIMLIB_EXTRACT_FLAG_UNIX_DATA 0x00000020 /** Do not extract security descriptors. This flag cannot be combined with * ::WIMLIB_EXTRACT_FLAG_STRICT_ACLS. */ #define WIMLIB_EXTRACT_FLAG_NO_ACLS 0x00000040 /** * Fail immediately if the full security descriptor of any file or directory * cannot be set exactly as specified in the WIM image. On Windows, the default * behavior without this flag when wimlib does not have permission to set the * correct security descriptor is to fall back to setting the security * descriptor with the SACL omitted, then with the DACL omitted, then with the * owner omitted, then not at all. This flag cannot be combined with * ::WIMLIB_EXTRACT_FLAG_NO_ACLS. */ #define WIMLIB_EXTRACT_FLAG_STRICT_ACLS 0x00000080 /** * This is the extraction equivalent to ::WIMLIB_ADD_FLAG_RPFIX. This forces * reparse-point fixups on, so absolute symbolic links or junction points will * be fixed to be absolute relative to the actual extraction root. Reparse- * point fixups are done by default for wimlib_extract_image() and * wimlib_extract_image_from_pipe() if WIM_HDR_FLAG_RP_FIX is set in the * WIM header. This flag cannot be combined with ::WIMLIB_EXTRACT_FLAG_NORPFIX. */ #define WIMLIB_EXTRACT_FLAG_RPFIX 0x00000100 /** Force reparse-point fixups on extraction off, regardless of the state of the * WIM_HDR_FLAG_RP_FIX flag in the WIM header. This flag cannot be combined * with ::WIMLIB_EXTRACT_FLAG_RPFIX. */ #define WIMLIB_EXTRACT_FLAG_NORPFIX 0x00000200 /** For wimlib_extract_paths() and wimlib_extract_pathlist() only: Extract the * paths, each of which must name a regular file, to standard output. */ #define WIMLIB_EXTRACT_FLAG_TO_STDOUT 0x00000400 /** * Instead of ignoring files and directories with names that cannot be * represented on the current platform (note: Windows has more restrictions on * filenames than POSIX-compliant systems), try to replace characters or append * junk to the names so that they can be extracted in some form. * * Note: this flag is unlikely to have any effect when extracting a WIM image * that was captured on Windows. */ #define WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES 0x00000800 /** * On Windows, when there exist two or more files with the same case insensitive * name but different case sensitive names, try to extract them all by appending * junk to the end of them, rather than arbitrarily extracting only one. * * Note: this flag is unlikely to have any effect when extracting a WIM image * that was captured on Windows. */ #define WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS 0x00001000 /** Do not ignore failure to set timestamps on extracted files. This flag * currently only has an effect when extracting to a directory on UNIX-like * systems. */ #define WIMLIB_EXTRACT_FLAG_STRICT_TIMESTAMPS 0x00002000 /** Do not ignore failure to set short names on extracted files. This flag * currently only has an effect on Windows. */ #define WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES 0x00004000 /** Do not ignore failure to extract symbolic links and junctions due to * permissions problems. This flag currently only has an effect on Windows. By * default, such failures are ignored since the default configuration of Windows * only allows the Administrator to create symbolic links. */ #define WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS 0x00008000 /** * For wimlib_extract_paths() and wimlib_extract_pathlist() only: Treat the * paths to extract as wildcard patterns ("globs") which may contain the * wildcard characters @c ? and @c *. The @c ? character matches any * non-path-separator character, whereas the @c * character matches zero or more * non-path-separator characters. Consequently, each glob may match zero or * more actual paths in the WIM image. * * By default, if a glob does not match any files, a warning but not an error * will be issued. This is the case even if the glob did not actually contain * wildcard characters. Use ::WIMLIB_EXTRACT_FLAG_STRICT_GLOB to get an error * instead. */ #define WIMLIB_EXTRACT_FLAG_GLOB_PATHS 0x00040000 /** In combination with ::WIMLIB_EXTRACT_FLAG_GLOB_PATHS, causes an error * (::WIMLIB_ERR_PATH_DOES_NOT_EXIST) rather than a warning to be issued when * one of the provided globs did not match a file. */ #define WIMLIB_EXTRACT_FLAG_STRICT_GLOB 0x00080000 /** * Do not extract Windows file attributes such as readonly, hidden, etc. * * This flag has an effect on Windows as well as in the NTFS-3G extraction mode. */ #define WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES 0x00100000 /** * For wimlib_extract_paths() and wimlib_extract_pathlist() only: Do not * preserve the directory structure of the archive when extracting --- that is, * place each extracted file or directory tree directly in the target directory. * The target directory will still be created if it does not already exist. */ #define WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE 0x00200000 /** * Windows only: Extract files as "pointers" back to the WIM archive. * * The effects of this option are fairly complex. See the documentation for the * --wimboot option of wimapply for more information. */ #define WIMLIB_EXTRACT_FLAG_WIMBOOT 0x00400000 /** * Since wimlib v1.8.2 and Windows-only: compress the extracted files using * System Compression, when possible. This only works on either Windows 10 or * later, or on an older Windows to which Microsoft's wofadk.sys driver has been * added. Several different compression formats may be used with System * Compression; this particular flag selects the XPRESS compression format with * 4096 byte chunks. */ #define WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K 0x01000000 /** Like ::WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K, but use XPRESS compression with * 8192 byte chunks. */ #define WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K 0x02000000 /** Like ::WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K, but use XPRESS compression with * 16384 byte chunks. */ #define WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K 0x04000000 /** Like ::WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K, but use LZX compression with * 32768 byte chunks. */ #define WIMLIB_EXTRACT_FLAG_COMPACT_LZX 0x08000000 /** @} */ /** @addtogroup G_mounting_wim_images * @{ */ /** Mount the WIM image read-write rather than the default of read-only. */ #define WIMLIB_MOUNT_FLAG_READWRITE 0x00000001 /** Enable FUSE debugging by passing the @c -d option to @c fuse_main(). */ #define WIMLIB_MOUNT_FLAG_DEBUG 0x00000002 /** Do not allow accessing named data streams in the mounted WIM image. */ #define WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE 0x00000004 /** Access named data streams in the mounted WIM image through extended file * attributes named "user.X", where X is the name of a data stream. This is the * default mode. */ #define WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR 0x00000008 /** Access named data streams in the mounted WIM image by specifying the file * name, a colon, then the name of the data stream. */ #define WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS 0x00000010 /** Support UNIX owners, groups, modes, and special files. */ #define WIMLIB_MOUNT_FLAG_UNIX_DATA 0x00000020 /** Allow other users to see the mounted filesystem. This passes the @c * allow_other option to fuse_main(). */ #define WIMLIB_MOUNT_FLAG_ALLOW_OTHER 0x00000040 /** @} */ /** @addtogroup G_creating_and_opening_wims * @{ */ /** Verify the WIM contents against the WIM's integrity table, if present. The * integrity table stores checksums for the raw data of the WIM file, divided * into fixed size chunks. Verification will compute checksums and compare them * with the stored values. If there are any mismatches, then * ::WIMLIB_ERR_INTEGRITY will be issued. If the WIM file does not contain an * integrity table, then this flag has no effect. */ #define WIMLIB_OPEN_FLAG_CHECK_INTEGRITY 0x00000001 /** Issue an error (::WIMLIB_ERR_IS_SPLIT_WIM) if the WIM is part of a split * WIM. Software can provide this flag for convenience if it explicitly does * not want to support split WIMs. */ #define WIMLIB_OPEN_FLAG_ERROR_IF_SPLIT 0x00000002 /** Check if the WIM is writable and issue an error * (::WIMLIB_ERR_WIM_IS_READONLY) if it is not. A WIM is considered writable * only if it is writable at the filesystem level, does not have the * WIM_HDR_FLAG_READONLY flag set in its header, and is not part of a * spanned set. It is not required to provide this flag before attempting to * make changes to the WIM, but with this flag you get an error immediately * rather than potentially much later, when wimlib_overwrite() is finally * called. */ #define WIMLIB_OPEN_FLAG_WRITE_ACCESS 0x00000004 /** @} */ /** @addtogroup G_mounting_wim_images * @{ */ /** Provide ::WIMLIB_WRITE_FLAG_CHECK_INTEGRITY when committing the WIM image. * Ignored if ::WIMLIB_UNMOUNT_FLAG_COMMIT not also specified. */ #define WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY 0x00000001 /** Commit changes to the read-write mounted WIM image. * If this flag is not specified, changes will be discarded. */ #define WIMLIB_UNMOUNT_FLAG_COMMIT 0x00000002 /** Provide ::WIMLIB_WRITE_FLAG_REBUILD when committing the WIM image. * Ignored if ::WIMLIB_UNMOUNT_FLAG_COMMIT not also specified. */ #define WIMLIB_UNMOUNT_FLAG_REBUILD 0x00000004 /** Provide ::WIMLIB_WRITE_FLAG_RECOMPRESS when committing the WIM image. * Ignored if ::WIMLIB_UNMOUNT_FLAG_COMMIT not also specified. */ #define WIMLIB_UNMOUNT_FLAG_RECOMPRESS 0x00000008 /** * In combination with ::WIMLIB_UNMOUNT_FLAG_COMMIT for a read-write mounted WIM * image, forces all file descriptors to the open WIM image to be closed before * committing it. * * Without ::WIMLIB_UNMOUNT_FLAG_COMMIT or with a read-only mounted WIM image, * this flag has no effect. */ #define WIMLIB_UNMOUNT_FLAG_FORCE 0x00000010 /** In combination with ::WIMLIB_UNMOUNT_FLAG_COMMIT for a read-write mounted * WIM image, causes the modified image to be committed to the WIM file as a * new, unnamed image appended to the archive. The original image in the WIM * file will be unmodified. */ #define WIMLIB_UNMOUNT_FLAG_NEW_IMAGE 0x00000020 /** @} */ /** @addtogroup G_modifying_wims * @{ */ /** Send ::WIMLIB_PROGRESS_MSG_UPDATE_BEGIN_COMMAND and * ::WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND messages. */ #define WIMLIB_UPDATE_FLAG_SEND_PROGRESS 0x00000001 /** @} */ /** @addtogroup G_writing_and_overwriting_wims * @{ */ /** * Include an integrity table in the resulting WIM file. * * For ::WIMStruct's created with wimlib_open_wim(), the default behavior is to * include an integrity table if and only if one was present before. For * ::WIMStruct's created with wimlib_create_new_wim(), the default behavior is * to not include an integrity table. */ #define WIMLIB_WRITE_FLAG_CHECK_INTEGRITY 0x00000001 /** * Do not include an integrity table in the resulting WIM file. This is the * default behavior, unless the ::WIMStruct was created by opening a WIM with an * integrity table. */ #define WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY 0x00000002 /** * Write the WIM as "pipable". After writing a WIM with this flag specified, * images from it can be applied directly from a pipe using * wimlib_extract_image_from_pipe(). See the documentation for the * --pipable option of wimcapture for more information. Beware: * WIMs written with this flag will not be compatible with Microsoft's software. * * For ::WIMStruct's created with wimlib_open_wim(), the default behavior is to * write the WIM as pipable if and only if it was pipable before. For * ::WIMStruct's created with wimlib_create_new_wim(), the default behavior is * to write the WIM as non-pipable. */ #define WIMLIB_WRITE_FLAG_PIPABLE 0x00000004 /** * Do not write the WIM as "pipable". This is the default behavior, unless the * ::WIMStruct was created by opening a pipable WIM. */ #define WIMLIB_WRITE_FLAG_NOT_PIPABLE 0x00000008 /** * When writing data to the WIM file, recompress it, even if the data is already * available in the desired compressed form (for example, in a WIM file from * which an image has been exported using wimlib_export_image()). * * ::WIMLIB_WRITE_FLAG_RECOMPRESS can be used to recompress with a higher * compression ratio for the same compression type and chunk size. Simply using * the default compression settings may suffice for this, especially if the WIM * file was created using another program/library that may not use as * sophisticated compression algorithms. Or, * wimlib_set_default_compression_level() can be called beforehand to set an * even higher compression level than the default. * * If the WIM contains solid resources, then ::WIMLIB_WRITE_FLAG_RECOMPRESS can * be used in combination with ::WIMLIB_WRITE_FLAG_SOLID to prevent any solid * resources from being re-used. Otherwise, solid resources are re-used * somewhat more liberally than normal compressed resources. * * ::WIMLIB_WRITE_FLAG_RECOMPRESS does not cause recompression of data * that would not otherwise be written. For example, a call to * wimlib_overwrite() with ::WIMLIB_WRITE_FLAG_RECOMPRESS will not, by itself, * cause already-existing data in the WIM file to be recompressed. To force the * WIM file to be fully rebuilt and recompressed, combine * ::WIMLIB_WRITE_FLAG_RECOMPRESS with ::WIMLIB_WRITE_FLAG_REBUILD. */ #define WIMLIB_WRITE_FLAG_RECOMPRESS 0x00000010 /** * Immediately before closing the WIM file, sync its data to disk. * * This flag forces the function to wait until the data is safely on disk before * returning success. Otherwise, modern operating systems tend to cache data * for some time (in some cases, 30+ seconds) before actually writing it to * disk, even after reporting to the application that the writes have succeeded. * * wimlib_overwrite() will set this flag automatically if it decides to * overwrite the WIM file via a temporary file instead of in-place. This is * necessary on POSIX systems; it will, for example, avoid problems with delayed * allocation on ext4. */ #define WIMLIB_WRITE_FLAG_FSYNC 0x00000020 /** * For wimlib_overwrite(): rebuild the entire WIM file, even if it otherwise * could be updated in-place by appending to it. Any data that existed in the * original WIM file but is not actually needed by any of the remaining images * will not be included. This can free up space left over after previous * in-place modifications to the WIM file. * * This flag can be combined with ::WIMLIB_WRITE_FLAG_RECOMPRESS to force all * data to be recompressed. Otherwise, compressed data is re-used if possible. * * wimlib_write() ignores this flag. */ #define WIMLIB_WRITE_FLAG_REBUILD 0x00000040 /** * For wimlib_overwrite(): override the default behavior after one or more calls * to wimlib_delete_image(), which is to rebuild the entire WIM file. With this * flag, only minimal changes to correctly remove the image from the WIM file * will be taken. This can be much faster, but it will result in the WIM file * getting larger rather than smaller. * * wimlib_write() ignores this flag. */ #define WIMLIB_WRITE_FLAG_SOFT_DELETE 0x00000080 /** * For wimlib_overwrite(), allow overwriting the WIM file even if the readonly * flag (WIM_HDR_FLAG_READONLY) is set in the WIM header. This can be * used following a call to wimlib_set_wim_info() with the * ::WIMLIB_CHANGE_READONLY_FLAG flag to actually set the readonly flag on the * on-disk WIM file. * * wimlib_write() ignores this flag. */ #define WIMLIB_WRITE_FLAG_IGNORE_READONLY_FLAG 0x00000100 /** * Do not include file data already present in other WIMs. This flag can be * used to write a "delta" WIM after the WIM files on which the delta is to be * based were referenced with wimlib_reference_resource_files() or * wimlib_reference_resources(). */ #define WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS 0x00000200 /** Deprecated; this flag should not be used outside of the library itself. */ #define WIMLIB_WRITE_FLAG_STREAMS_OK 0x00000400 /** * For wimlib_write(), retain the WIM's GUID instead of generating a new one. * * wimlib_overwrite() sets this by default, since the WIM remains, logically, * the same file. */ #define WIMLIB_WRITE_FLAG_RETAIN_GUID 0x00000800 /** * Concatenate files and compress them together, rather than compress each file * independently. This is also known as creating a "solid archive". This tends * to produce a better compression ratio at the cost of much slower random * access. * * WIM files created with this flag are only compatible with wimlib v1.6.0 or * later, WIMGAPI Windows 8 or later, and DISM Windows 8.1 or later. WIM files * created with this flag use a different version number in their header (3584 * instead of 68864) and are also called "ESD files". * * Note that providing this flag does not affect the "append by default" * behavior of wimlib_overwrite(). In other words, wimlib_overwrite() with just * ::WIMLIB_WRITE_FLAG_SOLID can be used to append solid-compressed data to a * WIM file that originally did not contain any solid-compressed data. But if * you instead want to rebuild and recompress an entire WIM file in solid mode, * then also provide ::WIMLIB_WRITE_FLAG_REBUILD and * ::WIMLIB_WRITE_FLAG_RECOMPRESS. * * Currently, new solid resources will, by default, be written using LZMS * compression with 64 MiB (67108864 byte) chunks. Use * wimlib_set_output_pack_compression_type() and/or * wimlib_set_output_pack_chunk_size() to change this. This is independent of * the WIM's main compression type and chunk size; you can have a WIM that * nominally uses LZX compression and 32768 byte chunks but actually contains * LZMS-compressed solid resources, for example. However, if including solid * resources, I suggest that you set the WIM's main compression type to LZMS as * well, either by creating the WIM with * ::wimlib_create_new_wim(::WIMLIB_COMPRESSION_TYPE_LZMS, ...) or by calling * ::wimlib_set_output_compression_type(..., ::WIMLIB_COMPRESSION_TYPE_LZMS). * * This flag will be set by default when writing or overwriting a WIM file that * either already contains solid resources, or has had solid resources exported * into it and the WIM's main compression type is LZMS. */ #define WIMLIB_WRITE_FLAG_SOLID 0x00001000 /** * Send ::WIMLIB_PROGRESS_MSG_DONE_WITH_FILE messages while writing the WIM * file. This is only needed in the unusual case that the library user needs to * know exactly when wimlib has read each file for the last time. */ #define WIMLIB_WRITE_FLAG_SEND_DONE_WITH_FILE_MESSAGES 0x00002000 /** * Do not consider content similarity when arranging file data for solid * compression. Providing this flag will typically worsen the compression * ratio, so only provide this flag if you know what you are doing. */ #define WIMLIB_WRITE_FLAG_NO_SOLID_SORT 0x00004000 /** * Since wimlib v1.8.3 and for wimlib_overwrite() only: unsafely compact * the WIM file in-place, without appending. Existing resources are shifted * down to fill holes and new resources are appended as needed. The WIM file is * truncated to its final size, which may shrink the on-disk file. This * operation cannot be safely interrupted. If the operation is interrupted, * then the WIM file will be corrupted, and it may be impossible (or at least * very difficult) to recover any data from it. Users of this flag are expected * to know what they are doing and assume responsibility for any data corruption * that may result. * * If the WIM file cannot be compacted in-place because of its structure, its * layout, or other requested write parameters, then wimlib_overwrite() fails * with ::WIMLIB_ERR_COMPACTION_NOT_POSSIBLE, and the caller may wish to retry * the operation without this flag. */ #define WIMLIB_WRITE_FLAG_UNSAFE_COMPACT 0x00008000 /** @} */ /** @addtogroup G_general * @{ */ /** Deprecated; no longer has any effect. */ #define WIMLIB_INIT_FLAG_ASSUME_UTF8 0x00000001 /** Windows-only: do not attempt to acquire additional privileges (currently * SeBackupPrivilege, SeRestorePrivilege, SeSecurityPrivilege, * SeTakeOwnershipPrivilege, and SeManageVolumePrivilege) when initializing the * library. This flag is intended for the case where the calling program * manages these privileges itself. Note: by default, no error is issued if * privileges cannot be acquired, although related errors may be reported later, * depending on if the operations performed actually require additional * privileges or not. */ #define WIMLIB_INIT_FLAG_DONT_ACQUIRE_PRIVILEGES 0x00000002 /** Windows only: If ::WIMLIB_INIT_FLAG_DONT_ACQUIRE_PRIVILEGES not specified, * return ::WIMLIB_ERR_INSUFFICIENT_PRIVILEGES if privileges that may be needed * to read all possible data and metadata for a capture operation could not be * acquired. Can be combined with ::WIMLIB_INIT_FLAG_STRICT_APPLY_PRIVILEGES. */ #define WIMLIB_INIT_FLAG_STRICT_CAPTURE_PRIVILEGES 0x00000004 /** Windows only: If ::WIMLIB_INIT_FLAG_DONT_ACQUIRE_PRIVILEGES not specified, * return ::WIMLIB_ERR_INSUFFICIENT_PRIVILEGES if privileges that may be needed * to restore all possible data and metadata for an apply operation could not be * acquired. Can be combined with ::WIMLIB_INIT_FLAG_STRICT_CAPTURE_PRIVILEGES. */ #define WIMLIB_INIT_FLAG_STRICT_APPLY_PRIVILEGES 0x00000008 /** Default to interpreting WIM paths case sensitively (default on UNIX-like * systems). */ #define WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE 0x00000010 /** Default to interpreting WIM paths case insensitively (default on Windows). * This does not apply to mounted images. */ #define WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE 0x00000020 /** @} */ /** @addtogroup G_nonstandalone_wims * @{ */ /** For wimlib_reference_resource_files(), enable shell-style filename globbing. * Ignored by wimlib_reference_resources(). */ #define WIMLIB_REF_FLAG_GLOB_ENABLE 0x00000001 /** For wimlib_reference_resource_files(), issue an error * (::WIMLIB_ERR_GLOB_HAD_NO_MATCHES) if a glob did not match any files. The * default behavior without this flag is to issue no error at that point, but * then attempt to open the glob as a literal path, which of course will fail * anyway if no file exists at that path. No effect if * ::WIMLIB_REF_FLAG_GLOB_ENABLE is not also specified. Ignored by * wimlib_reference_resources(). */ #define WIMLIB_REF_FLAG_GLOB_ERR_ON_NOMATCH 0x00000002 /** @} */ /** @addtogroup G_modifying_wims * @{ */ /** The specific type of update to perform. */ enum wimlib_update_op { /** Add a new file or directory tree to the image. */ WIMLIB_UPDATE_OP_ADD = 0, /** Delete a file or directory tree from the image. */ WIMLIB_UPDATE_OP_DELETE = 1, /** Rename a file or directory tree in the image. */ WIMLIB_UPDATE_OP_RENAME = 2, }; /** Data for a ::WIMLIB_UPDATE_OP_ADD operation. */ struct wimlib_add_command { /** Filesystem path to the file or directory tree to add. */ wimlib_tchar *fs_source_path; /** Destination path in the image. To specify the root directory of the * image, use ::WIMLIB_WIM_ROOT_PATH. */ wimlib_tchar *wim_target_path; /** Path to capture configuration file to use, or @c NULL if not * specified. */ wimlib_tchar *config_file; /** Bitwise OR of WIMLIB_ADD_FLAG_* flags. */ int add_flags; }; /** Data for a ::WIMLIB_UPDATE_OP_DELETE operation. */ struct wimlib_delete_command { /** The path to the file or directory within the image to delete. */ wimlib_tchar *wim_path; /** Bitwise OR of WIMLIB_DELETE_FLAG_* flags. */ int delete_flags; }; /** Data for a ::WIMLIB_UPDATE_OP_RENAME operation. */ struct wimlib_rename_command { /** The path to the source file or directory within the image. */ wimlib_tchar *wim_source_path; /** The path to the destination file or directory within the image. */ wimlib_tchar *wim_target_path; /** Reserved; set to 0. */ int rename_flags; }; /** Specification of an update to perform on a WIM image. */ struct wimlib_update_command { enum wimlib_update_op op; union { struct wimlib_add_command add; struct wimlib_delete_command delete_; /* Underscore is for C++ compatibility. */ struct wimlib_rename_command rename; }; }; /** @} */ /** @addtogroup G_general * @{ */ /** * Possible values of the error code returned by many functions in wimlib. * * See the documentation for each wimlib function to see specifically what error * codes can be returned by a given function, and what they mean. */ enum wimlib_error_code { WIMLIB_ERR_SUCCESS = 0, WIMLIB_ERR_ALREADY_LOCKED = 1, WIMLIB_ERR_DECOMPRESSION = 2, WIMLIB_ERR_FUSE = 6, WIMLIB_ERR_GLOB_HAD_NO_MATCHES = 8, WIMLIB_ERR_IMAGE_COUNT = 10, WIMLIB_ERR_IMAGE_NAME_COLLISION = 11, WIMLIB_ERR_INSUFFICIENT_PRIVILEGES = 12, WIMLIB_ERR_INTEGRITY = 13, WIMLIB_ERR_INVALID_CAPTURE_CONFIG = 14, WIMLIB_ERR_INVALID_CHUNK_SIZE = 15, WIMLIB_ERR_INVALID_COMPRESSION_TYPE = 16, WIMLIB_ERR_INVALID_HEADER = 17, WIMLIB_ERR_INVALID_IMAGE = 18, WIMLIB_ERR_INVALID_INTEGRITY_TABLE = 19, WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY = 20, WIMLIB_ERR_INVALID_METADATA_RESOURCE = 21, WIMLIB_ERR_INVALID_OVERLAY = 23, WIMLIB_ERR_INVALID_PARAM = 24, WIMLIB_ERR_INVALID_PART_NUMBER = 25, WIMLIB_ERR_INVALID_PIPABLE_WIM = 26, WIMLIB_ERR_INVALID_REPARSE_DATA = 27, WIMLIB_ERR_INVALID_RESOURCE_HASH = 28, WIMLIB_ERR_INVALID_UTF16_STRING = 30, WIMLIB_ERR_INVALID_UTF8_STRING = 31, WIMLIB_ERR_IS_DIRECTORY = 32, WIMLIB_ERR_IS_SPLIT_WIM = 33, WIMLIB_ERR_LINK = 35, WIMLIB_ERR_METADATA_NOT_FOUND = 36, WIMLIB_ERR_MKDIR = 37, WIMLIB_ERR_MQUEUE = 38, WIMLIB_ERR_NOMEM = 39, WIMLIB_ERR_NOTDIR = 40, WIMLIB_ERR_NOTEMPTY = 41, WIMLIB_ERR_NOT_A_REGULAR_FILE = 42, WIMLIB_ERR_NOT_A_WIM_FILE = 43, WIMLIB_ERR_NOT_PIPABLE = 44, WIMLIB_ERR_NO_FILENAME = 45, WIMLIB_ERR_NTFS_3G = 46, WIMLIB_ERR_OPEN = 47, WIMLIB_ERR_OPENDIR = 48, WIMLIB_ERR_PATH_DOES_NOT_EXIST = 49, WIMLIB_ERR_READ = 50, WIMLIB_ERR_READLINK = 51, WIMLIB_ERR_RENAME = 52, WIMLIB_ERR_REPARSE_POINT_FIXUP_FAILED = 54, WIMLIB_ERR_RESOURCE_NOT_FOUND = 55, WIMLIB_ERR_RESOURCE_ORDER = 56, WIMLIB_ERR_SET_ATTRIBUTES = 57, WIMLIB_ERR_SET_REPARSE_DATA = 58, WIMLIB_ERR_SET_SECURITY = 59, WIMLIB_ERR_SET_SHORT_NAME = 60, WIMLIB_ERR_SET_TIMESTAMPS = 61, WIMLIB_ERR_SPLIT_INVALID = 62, WIMLIB_ERR_STAT = 63, WIMLIB_ERR_UNEXPECTED_END_OF_FILE = 65, WIMLIB_ERR_UNICODE_STRING_NOT_REPRESENTABLE = 66, WIMLIB_ERR_UNKNOWN_VERSION = 67, WIMLIB_ERR_UNSUPPORTED = 68, WIMLIB_ERR_UNSUPPORTED_FILE = 69, WIMLIB_ERR_WIM_IS_READONLY = 71, WIMLIB_ERR_WRITE = 72, WIMLIB_ERR_XML = 73, WIMLIB_ERR_WIM_IS_ENCRYPTED = 74, WIMLIB_ERR_WIMBOOT = 75, WIMLIB_ERR_ABORTED_BY_PROGRESS = 76, WIMLIB_ERR_UNKNOWN_PROGRESS_STATUS = 77, WIMLIB_ERR_MKNOD = 78, WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY = 79, WIMLIB_ERR_NOT_A_MOUNTPOINT = 80, WIMLIB_ERR_NOT_PERMITTED_TO_UNMOUNT = 81, WIMLIB_ERR_FVE_LOCKED_VOLUME = 82, WIMLIB_ERR_UNABLE_TO_READ_CAPTURE_CONFIG = 83, WIMLIB_ERR_WIM_IS_INCOMPLETE = 84, WIMLIB_ERR_COMPACTION_NOT_POSSIBLE = 85, WIMLIB_ERR_IMAGE_HAS_MULTIPLE_REFERENCES = 86, WIMLIB_ERR_DUPLICATE_EXPORTED_IMAGE = 87, WIMLIB_ERR_CONCURRENT_MODIFICATION_DETECTED = 88, WIMLIB_ERR_SNAPSHOT_FAILURE = 89, WIMLIB_ERR_INVALID_XATTR = 90, WIMLIB_ERR_SET_XATTR = 91, }; /** Used to indicate no image or an invalid image. */ #define WIMLIB_NO_IMAGE 0 /** Used to specify all images in the WIM. */ #define WIMLIB_ALL_IMAGES (-1) /** @} */ /** * @ingroup G_modifying_wims * * Append an empty image to a ::WIMStruct. * * The new image will initially contain no files or directories, although if * written without further modifications, then a root directory will be created * automatically for it. * * After calling this function, you can use wimlib_update_image() to add files * to the new image. This gives you more control over making the new image * compared to calling wimlib_add_image() or wimlib_add_image_multisource(). * * @param wim * Pointer to the ::WIMStruct to which to add the image. * @param name * Name to give the new image. If @c NULL or empty, the new image is given * no name. If nonempty, it must specify a name that does not already * exist in @p wim. * @param new_idx_ret * If non-NULL, the index of the newly added image is returned in * this location. * * @return 0 on success; a ::wimlib_error_code value on failure. * * @retval ::WIMLIB_ERR_IMAGE_NAME_COLLISION * The WIM already contains an image with the requested name. */ extern int wimlib_add_empty_image(WIMStruct *wim, const wimlib_tchar *name, int *new_idx_ret); /** * @ingroup G_modifying_wims * * Add an image to a ::WIMStruct from an on-disk directory tree or NTFS volume. * * The directory tree or NTFS volume is scanned immediately to load the dentry * tree into memory, and file metadata is read. However, actual file data may * not be read until the ::WIMStruct is persisted to disk using wimlib_write() * or wimlib_overwrite(). * * See the documentation for the @b wimlib-imagex program for more information * about the "normal" capture mode versus the NTFS capture mode (entered by * providing the flag ::WIMLIB_ADD_FLAG_NTFS). * * Note that no changes are committed to disk until wimlib_write() or * wimlib_overwrite() is called. * * @param wim * Pointer to the ::WIMStruct to which to add the image. * @param source * A path to a directory or unmounted NTFS volume that will be captured as * a WIM image. * @param name * Name to give the new image. If @c NULL or empty, the new image is given * no name. If nonempty, it must specify a name that does not already * exist in @p wim. * @param config_file * Path to capture configuration file, or @c NULL. This file may specify, * among other things, which files to exclude from capture. See the * documentation for wimcapture (--config option) for details * of the file format. If @c NULL, the default capture configuration will * be used. Ordinarily, the default capture configuration will result in * no files being excluded from capture purely based on name; however, the * ::WIMLIB_ADD_FLAG_WINCONFIG and ::WIMLIB_ADD_FLAG_WIMBOOT flags modify * the default. * @param add_flags * Bitwise OR of flags prefixed with WIMLIB_ADD_FLAG. * * @return 0 on success; a ::wimlib_error_code value on failure. * * This function is implemented by calling wimlib_add_empty_image(), then * calling wimlib_update_image() with a single "add" command, so any error code * returned by wimlib_add_empty_image() may be returned, as well as any error * codes returned by wimlib_update_image() other than ones documented as only * being returned specifically by an update involving delete or rename commands. * * If a progress function is registered with @p wim, then it will receive the * messages ::WIMLIB_PROGRESS_MSG_SCAN_BEGIN and ::WIMLIB_PROGRESS_MSG_SCAN_END. * In addition, if ::WIMLIB_ADD_FLAG_VERBOSE is specified in @p add_flags, it * will receive ::WIMLIB_PROGRESS_MSG_SCAN_DENTRY. */ extern int wimlib_add_image(WIMStruct *wim, const wimlib_tchar *source, const wimlib_tchar *name, const wimlib_tchar *config_file, int add_flags); /** * @ingroup G_modifying_wims * * This function is equivalent to wimlib_add_image() except it allows for * multiple sources to be combined into a single WIM image. This is done by * specifying the @p sources and @p num_sources parameters instead of the @p * source parameter of wimlib_add_image(). The rest of the parameters are the * same as wimlib_add_image(). See the documentation for wimcapture for * full details on how this mode works. */ extern int wimlib_add_image_multisource(WIMStruct *wim, const struct wimlib_capture_source *sources, size_t num_sources, const wimlib_tchar *name, const wimlib_tchar *config_file, int add_flags); /** * @ingroup G_modifying_wims * * Add the file or directory tree at @p fs_source_path on the filesystem to the * location @p wim_target_path within the specified @p image of the @p wim. * * This just builds an appropriate ::wimlib_add_command and passes it to * wimlib_update_image(). */ extern int wimlib_add_tree(WIMStruct *wim, int image, const wimlib_tchar *fs_source_path, const wimlib_tchar *wim_target_path, int add_flags); /** * @ingroup G_creating_and_opening_wims * * Create a ::WIMStruct which initially contains no images and is not backed by * an on-disk file. * * @param ctype * The "output compression type" to assign to the ::WIMStruct. This is the * compression type that will be used if the ::WIMStruct is later persisted * to an on-disk file using wimlib_write(). *
* This choice is not necessarily final. If desired, it can still be * changed at any time before wimlib_write() is called, using * wimlib_set_output_compression_type(). In addition, if you wish to use a * non-default compression chunk size, then you will need to call * wimlib_set_output_chunk_size(). * @param wim_ret * On success, a pointer to the new ::WIMStruct is written to the memory * location pointed to by this parameter. This ::WIMStruct must be freed * using using wimlib_free() when finished with it. * * @return 0 on success; a ::wimlib_error_code value on failure. * * @retval ::WIMLIB_ERR_INVALID_COMPRESSION_TYPE * @p ctype was not a supported compression type. * @retval ::WIMLIB_ERR_NOMEM * Insufficient memory to allocate a new ::WIMStruct. */ extern int wimlib_create_new_wim(enum wimlib_compression_type ctype, WIMStruct **wim_ret); /** * @ingroup G_modifying_wims * * Delete an image, or all images, from a ::WIMStruct. * * Note that no changes are committed to disk until wimlib_write() or * wimlib_overwrite() is called. * * @param wim * Pointer to the ::WIMStruct from which to delete the image. * @param image * The 1-based index of the image to delete, or ::WIMLIB_ALL_IMAGES to * delete all images. * * @return 0 on success; a ::wimlib_error_code value on failure. * * @retval ::WIMLIB_ERR_INVALID_IMAGE * @p image does not exist in the WIM. * * This function can additionally return ::WIMLIB_ERR_DECOMPRESSION, * ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_METADATA_NOT_FOUND, * ::WIMLIB_ERR_READ, or ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE, all of which * indicate failure (for different reasons) to read the metadata resource for an * image that needed to be deleted. * * If this function fails when @p image was ::WIMLIB_ALL_IMAGES, then it's * possible that some but not all of the images were deleted. */ extern int wimlib_delete_image(WIMStruct *wim, int image); /** * @ingroup G_modifying_wims * * Delete the @p path from the specified @p image of the @p wim. * * This just builds an appropriate ::wimlib_delete_command and passes it to * wimlib_update_image(). */ extern int wimlib_delete_path(WIMStruct *wim, int image, const wimlib_tchar *path, int delete_flags); /** * @ingroup G_modifying_wims * * Export an image, or all images, from a ::WIMStruct into another ::WIMStruct. * * Specifically, if the destination ::WIMStruct contains n images, then * the source image(s) will be appended, in order, starting at destination index * n + 1. By default, all image metadata will be exported verbatim, * but certain changes can be made by passing appropriate parameters. * * wimlib_export_image() is only an in-memory operation; no changes are * committed to disk until wimlib_write() or wimlib_overwrite() is called. * * A limitation of the current implementation of wimlib_export_image() is that * the directory tree of a source or destination image cannot be updated * following an export until one of the two images has been freed from memory. * * @param src_wim * The WIM from which to export the images, specified as a pointer to the * ::WIMStruct for a standalone WIM file, a delta WIM file, or part 1 of a * split WIM. In the case of a WIM file that is not standalone, this * ::WIMStruct must have had any needed external resources previously * referenced using wimlib_reference_resources() or * wimlib_reference_resource_files(). * @param src_image * The 1-based index of the image from @p src_wim to export, or * ::WIMLIB_ALL_IMAGES. * @param dest_wim * The ::WIMStruct to which to export the images. * @param dest_name * For single-image exports, the name to give the exported image in @p * dest_wim. If left @c NULL, the name from @p src_wim is used. For * ::WIMLIB_ALL_IMAGES exports, this parameter must be left @c NULL; in * that case, the names are all taken from @p src_wim. This parameter is * overridden by ::WIMLIB_EXPORT_FLAG_NO_NAMES. * @param dest_description * For single-image exports, the description to give the exported image in * the new WIM file. If left @c NULL, the description from @p src_wim is * used. For ::WIMLIB_ALL_IMAGES exports, this parameter must be left @c * NULL; in that case, the description are all taken from @p src_wim. This * parameter is overridden by ::WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS. * @param export_flags * Bitwise OR of flags prefixed with WIMLIB_EXPORT_FLAG. * * @return 0 on success; a ::wimlib_error_code value on failure. * * @retval ::WIMLIB_ERR_DUPLICATE_EXPORTED_IMAGE * One or more of the source images had already been exported into the * destination WIM. * @retval ::WIMLIB_ERR_IMAGE_NAME_COLLISION * One or more of the names being given to an exported image was already in * use in the destination WIM. * @retval ::WIMLIB_ERR_INVALID_IMAGE * @p src_image does not exist in @p src_wim. * @retval ::WIMLIB_ERR_METADATA_NOT_FOUND * At least one of @p src_wim and @p dest_wim does not contain image * metadata; for example, one of them represents a non-first part of a * split WIM. * @retval ::WIMLIB_ERR_RESOURCE_NOT_FOUND * A file data blob that needed to be exported could not be found in the * blob lookup table of @p src_wim. See @ref G_nonstandalone_wims. * * This function can additionally return ::WIMLIB_ERR_DECOMPRESSION, * ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_METADATA_NOT_FOUND, * ::WIMLIB_ERR_READ, or ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE, all of which * indicate failure (for different reasons) to read the metadata resource for an * image in @p src_wim that needed to be exported. */ extern int wimlib_export_image(WIMStruct *src_wim, int src_image, WIMStruct *dest_wim, const wimlib_tchar *dest_name, const wimlib_tchar *dest_description, int export_flags); /** * @ingroup G_extracting_wims * * Extract an image, or all images, from a ::WIMStruct. * * The exact behavior of how wimlib extracts files from a WIM image is * controllable by the @p extract_flags parameter, but there also are * differences depending on the platform (UNIX-like vs Windows). See the * documentation for wimapply for more information, including about the * NTFS-3G extraction mode. * * @param wim * The WIM from which to extract the image(s), specified as a pointer to the * ::WIMStruct for a standalone WIM file, a delta WIM file, or part 1 of a * split WIM. In the case of a WIM file that is not standalone, this * ::WIMStruct must have had any needed external resources previously * referenced using wimlib_reference_resources() or * wimlib_reference_resource_files(). * @param image * The 1-based index of the image to extract, or ::WIMLIB_ALL_IMAGES to * extract all images. Note: ::WIMLIB_ALL_IMAGES is unsupported in NTFS-3G * extraction mode. * @param target * A null-terminated string which names the location to which the image(s) * will be extracted. By default, this is interpreted as a path to a * directory. Alternatively, if ::WIMLIB_EXTRACT_FLAG_NTFS is specified in * @p extract_flags, then this is interpreted as a path to an unmounted * NTFS volume. * @param extract_flags * Bitwise OR of flags prefixed with WIMLIB_EXTRACT_FLAG. * * @return 0 on success; a ::wimlib_error_code value on failure. * * @retval ::WIMLIB_ERR_DECOMPRESSION * The WIM file contains invalid compressed data. * @retval ::WIMLIB_ERR_INVALID_IMAGE * @p image does not exist in @p wim. * @retval ::WIMLIB_ERR_INVALID_METADATA_RESOURCE * The metadata for an image to extract was invalid. * @retval ::WIMLIB_ERR_INVALID_PARAM * The extraction flags were invalid; more details may be found in the * documentation for the specific extraction flags that were specified. Or * @p target was @c NULL or an empty string, or @p wim was @c NULL. * @retval ::WIMLIB_ERR_INVALID_RESOURCE_HASH * The data of a file that needed to be extracted was corrupt. * @retval ::WIMLIB_ERR_LINK * Failed to create a symbolic link or a hard link. * @retval ::WIMLIB_ERR_METADATA_NOT_FOUND * @p wim does not contain image metadata; for example, it represents a * non-first part of a split WIM. * @retval ::WIMLIB_ERR_MKDIR * Failed create a directory. * @retval ::WIMLIB_ERR_NTFS_3G * libntfs-3g reported that a problem occurred while writing to the NTFS * volume. * @retval ::WIMLIB_ERR_OPEN * Could not create a file, or failed to open an already-extracted file. * @retval ::WIMLIB_ERR_READ * Failed to read data from the WIM. * @retval ::WIMLIB_ERR_READLINK * Failed to determine the target of a symbolic link in the WIM. * @retval ::WIMLIB_ERR_REPARSE_POINT_FIXUP_FAILED * Failed to fix the target of an absolute symbolic link (e.g. if the * target would have exceeded the maximum allowed length). (Only if * reparse data was supported by the extraction mode and * ::WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS was specified in @p * extract_flags.) * @retval ::WIMLIB_ERR_RESOURCE_NOT_FOUND * A file data blob that needed to be extracted could not be found in the * blob lookup table of @p wim. See @ref G_nonstandalone_wims. * @retval ::WIMLIB_ERR_SET_ATTRIBUTES * Failed to set attributes on a file. * @retval ::WIMLIB_ERR_SET_REPARSE_DATA * Failed to set reparse data on a file (only if reparse data was supported * by the extraction mode). * @retval ::WIMLIB_ERR_SET_SECURITY * Failed to set security descriptor on a file. * @retval ::WIMLIB_ERR_SET_SHORT_NAME * Failed to set the short name of a file. * @retval ::WIMLIB_ERR_SET_TIMESTAMPS * Failed to set timestamps on a file. * @retval ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE * Unexpected end-of-file occurred when reading data from the WIM. * @retval ::WIMLIB_ERR_UNSUPPORTED * A requested extraction flag, or the data or metadata that must be * extracted to support it, is unsupported in the build and configuration * of wimlib, or on the current platform or extraction mode or target * volume. Flags affected by this include ::WIMLIB_EXTRACT_FLAG_NTFS, * ::WIMLIB_EXTRACT_FLAG_UNIX_DATA, ::WIMLIB_EXTRACT_FLAG_STRICT_ACLS, * ::WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES, * ::WIMLIB_EXTRACT_FLAG_STRICT_TIMESTAMPS, and * ::WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS. For example, if * ::WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES is specified in @p * extract_flags, ::WIMLIB_ERR_UNSUPPORTED will be returned if the WIM * image contains one or more files with short names, but extracting short * names is not supported --- on Windows, this occurs if the target volume * does not support short names, while on non-Windows, this occurs if * ::WIMLIB_EXTRACT_FLAG_NTFS was not specified in @p extract_flags. * @retval ::WIMLIB_ERR_WIMBOOT * ::WIMLIB_EXTRACT_FLAG_WIMBOOT was specified in @p extract_flags, but * there was a problem creating WIMBoot pointer files or registering a * source WIM file with the Windows Overlay Filesystem (WOF) driver. * @retval ::WIMLIB_ERR_WRITE * Failed to write data to a file being extracted. * * If a progress function is registered with @p wim, then as each image is * extracted it will receive ::WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN, then * zero or more ::WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE messages, then zero * or more ::WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS messages, then zero or more * ::WIMLIB_PROGRESS_MSG_EXTRACT_METADATA messages, then * ::WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END. */ extern int wimlib_extract_image(WIMStruct *wim, int image, const wimlib_tchar *target, int extract_flags); /** * @ingroup G_extracting_wims * * Extract one image from a pipe on which a pipable WIM is being sent. * * See the documentation for ::WIMLIB_WRITE_FLAG_PIPABLE, and @ref * subsec_pipable_wims, for more information about pipable WIMs. * * This function operates in a special way to read the WIM fully sequentially. * As a result, there is no ::WIMStruct is made visible to library users, and * you cannot call wimlib_open_wim() on the pipe. (You can, however, use * wimlib_open_wim() to transparently open a pipable WIM if it's available as a * seekable file, not a pipe.) * * @param pipe_fd * File descriptor, which may be a pipe, opened for reading and positioned * at the start of the pipable WIM. * @param image_num_or_name * String that specifies the 1-based index or name of the image to extract. * It is translated to an image index using the same rules that * wimlib_resolve_image() uses. However, unlike wimlib_extract_image(), * only a single image (not all images) can be specified. Alternatively, * specify @p NULL here to use the first image in the WIM if it contains * exactly one image but otherwise return ::WIMLIB_ERR_INVALID_IMAGE. * @param target * Same as the corresponding parameter to wimlib_extract_image(). * @param extract_flags * Same as the corresponding parameter to wimlib_extract_image(). * * @return 0 on success; a ::wimlib_error_code value on failure. The possible * error codes include those returned by wimlib_extract_image() and * wimlib_open_wim() as well as the following: * * @retval ::WIMLIB_ERR_INVALID_PIPABLE_WIM * Data read from the pipable WIM was invalid. * @retval ::WIMLIB_ERR_NOT_PIPABLE * The WIM being piped over @p pipe_fd is a normal WIM, not a pipable WIM. */ extern int wimlib_extract_image_from_pipe(int pipe_fd, const wimlib_tchar *image_num_or_name, const wimlib_tchar *target, int extract_flags); /** * @ingroup G_extracting_wims * * Same as wimlib_extract_image_from_pipe(), but allows specifying a progress * function. The progress function will be used while extracting the image and * will receive the normal extraction progress messages, such as * ::WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS, in addition to * ::WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN. */ extern int wimlib_extract_image_from_pipe_with_progress(int pipe_fd, const wimlib_tchar *image_num_or_name, const wimlib_tchar *target, int extract_flags, wimlib_progress_func_t progfunc, void *progctx); /** * @ingroup G_extracting_wims * * Similar to wimlib_extract_paths(), but the paths to extract from the WIM * image are specified in the ASCII, UTF-8, or UTF-16LE text file named by @p * path_list_file which itself contains the list of paths to use, one per line. * Leading and trailing whitespace is ignored. Empty lines and lines beginning * with the ';' or '#' characters are ignored. No quotes are needed, as paths * are otherwise delimited by the newline character. However, quotes will be * stripped if present. * * If @p path_list_file is @c NULL, then the pathlist file is read from standard * input. * * The error codes are the same as those returned by wimlib_extract_paths(), * except that wimlib_extract_pathlist() returns an appropriate error code if it * cannot read the path list file (e.g. ::WIMLIB_ERR_OPEN, ::WIMLIB_ERR_STAT, * ::WIMLIB_ERR_READ). */ extern int wimlib_extract_pathlist(WIMStruct *wim, int image, const wimlib_tchar *target, const wimlib_tchar *path_list_file, int extract_flags); /** * @ingroup G_extracting_wims * * Extract zero or more paths (files or directory trees) from the specified WIM * image. * * By default, each path will be extracted to a corresponding subdirectory of * the target based on its location in the image. For example, if one of the * paths to extract is /Windows/explorer.exe and the target is * outdir, the file will be extracted to * outdir/Windows/explorer.exe. This behavior can be changed by * providing the flag ::WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE, which * will cause each file or directory tree to be placed directly in the target * directory --- so the same example would extract /Windows/explorer.exe * to outdir/explorer.exe. * * With globbing turned off (the default), paths are always checked for * existence strictly; that is, if any path to extract does not exist in the * image, then nothing is extracted and the function fails with * ::WIMLIB_ERR_PATH_DOES_NOT_EXIST. But with globbing turned on * (::WIMLIB_EXTRACT_FLAG_GLOB_PATHS specified), globs are by default permitted * to match no files, and there is a flag (::WIMLIB_EXTRACT_FLAG_STRICT_GLOB) to * enable the strict behavior if desired. * * Symbolic links are not dereferenced when paths in the image are interpreted. * * @param wim * WIM from which to extract the paths, specified as a pointer to the * ::WIMStruct for a standalone WIM file, a delta WIM file, or part 1 of a * split WIM. In the case of a WIM file that is not standalone, this * ::WIMStruct must have had any needed external resources previously * referenced using wimlib_reference_resources() or * wimlib_reference_resource_files(). * @param image * The 1-based index of the WIM image from which to extract the paths. * @param paths * Array of paths to extract. Each element must be the absolute path to a * file or directory within the image. Path separators may be either * forwards or backwards slashes, and leading path separators are optional. * The paths will be interpreted either case-sensitively (UNIX default) or * case-insensitively (Windows default); however, the case sensitivity can * be configured explicitly at library initialization time by passing an * appropriate flag to wimlib_global_init(). *
* By default, "globbing" is disabled, so the characters @c * and @c ? are * interpreted literally. This can be changed by specifying * ::WIMLIB_EXTRACT_FLAG_GLOB_PATHS in @p extract_flags. * @param num_paths * Number of paths specified in @p paths. * @param target * Directory to which to extract the paths. * @param extract_flags * Bitwise OR of flags prefixed with WIMLIB_EXTRACT_FLAG. * * @return 0 on success; a ::wimlib_error_code value on failure. Most of the * error codes are the same as those returned by wimlib_extract_image(). Below, * some of the error codes returned in situations specific to path-mode * extraction are documented: * * @retval ::WIMLIB_ERR_NOT_A_REGULAR_FILE * ::WIMLIB_EXTRACT_FLAG_TO_STDOUT was specified in @p extract_flags, but * one of the paths to extract did not name a regular file. * @retval ::WIMLIB_ERR_PATH_DOES_NOT_EXIST * One of the paths to extract does not exist in the image; see discussion * above about strict vs. non-strict behavior. * * If a progress function is registered with @p wim, then it will receive * ::WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS. */ extern int wimlib_extract_paths(WIMStruct *wim, int image, const wimlib_tchar *target, const wimlib_tchar * const *paths, size_t num_paths, int extract_flags); /** * @ingroup G_wim_information * * Similar to wimlib_get_xml_data(), but the XML document will be written to the * specified standard C FILE* instead of retrieved in an in-memory * buffer. * * @return 0 on success; a ::wimlib_error_code value on failure. This may * return any error code which can be returned by wimlib_get_xml_data() as well * as the following error codes: * * @retval ::WIMLIB_ERR_WRITE * Failed to write the data to the requested file. */ extern int wimlib_extract_xml_data(WIMStruct *wim, FILE *fp); /** * @ingroup G_general * * Release a reference to a ::WIMStruct. If the ::WIMStruct is still referenced * by other ::WIMStruct's (e.g. following calls to wimlib_export_image() or * wimlib_reference_resources()), then the library will free it later, when the * last reference is released; otherwise it is freed immediately and any * associated file descriptors are closed. * * @param wim * Pointer to the ::WIMStruct to release. If @c NULL, no action is taken. */ extern void wimlib_free(WIMStruct *wim); /** * @ingroup G_general * * Convert a ::wimlib_compression_type value into a string. * * @param ctype * The compression type value to convert. * * @return * A statically allocated string naming the compression type, such as * "None", "LZX", or "XPRESS". If the value was unrecognized, then * the resulting string will be "Invalid". */ extern const wimlib_tchar * wimlib_get_compression_type_string(enum wimlib_compression_type ctype); /** * @ingroup G_general * * Convert a wimlib error code into a string describing it. * * @param code * An error code returned by one of wimlib's functions. * * @return * Pointer to a statically allocated string describing the error code. If * the value was unrecognized, then the resulting string will be "Unknown * error". */ extern const wimlib_tchar * wimlib_get_error_string(enum wimlib_error_code code); /** * @ingroup G_wim_information * * Get the description of the specified image. Equivalent to * wimlib_get_image_property(wim, image, "DESCRIPTION"). */ extern const wimlib_tchar * wimlib_get_image_description(const WIMStruct *wim, int image); /** * @ingroup G_wim_information * * Get the name of the specified image. Equivalent to * wimlib_get_image_property(wim, image, "NAME"), except that * wimlib_get_image_name() will return an empty string if the image is unnamed * whereas wimlib_get_image_property() may return @c NULL in that case. */ extern const wimlib_tchar * wimlib_get_image_name(const WIMStruct *wim, int image); /** * @ingroup G_wim_information * * Since wimlib v1.8.3: get a per-image property from the WIM's XML document. * This is an alternative to wimlib_get_image_name() and * wimlib_get_image_description() which allows getting any simple string * property. * * @param wim * Pointer to the ::WIMStruct for the WIM. * @param image * The 1-based index of the image for which to get the property. * @param property_name * The name of the image property, for example "NAME", "DESCRIPTION", or * "TOTALBYTES". The name can contain forward slashes to indicate a nested * XML element; for example, "WINDOWS/VERSION/BUILD" indicates the BUILD * element nested within the VERSION element nested within the WINDOWS * element. Since wimlib v1.9.0, a bracketed number can be used to * indicate one of several identically-named elements; for example, * "WINDOWS/LANGUAGES/LANGUAGE[2]" indicates the second "LANGUAGE" element * nested within the "WINDOWS/LANGUAGES" element. Note that element names * are case sensitive. * * @return * The property's value as a ::wimlib_tchar string, or @c NULL if there is * no such property. The string may not remain valid after later library * calls, so the caller should duplicate it if needed. */ extern const wimlib_tchar * wimlib_get_image_property(const WIMStruct *wim, int image, const wimlib_tchar *property_name); /** * @ingroup G_general * * Return the version of wimlib as a 32-bit number whose top 12 bits contain the * major version, the next 10 bits contain the minor version, and the low 10 * bits contain the patch version. * * In other words, the returned value is equal to ((WIMLIB_MAJOR_VERSION << * 20) | (WIMLIB_MINOR_VERSION << 10) | WIMLIB_PATCH_VERSION) for the * corresponding header file. */ extern uint32_t wimlib_get_version(void); /** * @ingroup G_general * * Since wimlib v1.13.0: like wimlib_get_version(), but returns the full * PACKAGE_VERSION string that was set at build time. (This allows a beta * release to be distinguished from an official release.) */ extern const wimlib_tchar * wimlib_get_version_string(void); /** * @ingroup G_wim_information * * Get basic information about a WIM file. * * @param wim * Pointer to the ::WIMStruct to query. This need not represent a * standalone WIM (e.g. it could represent part of a split WIM). * @param info * A ::wimlib_wim_info structure that will be filled in with information * about the WIM file. * * @return 0 */ extern int wimlib_get_wim_info(WIMStruct *wim, struct wimlib_wim_info *info); /** * @ingroup G_wim_information * * Read a WIM file's XML document into an in-memory buffer. * * The XML document contains metadata about the WIM file and the images stored * in it. * * @param wim * Pointer to the ::WIMStruct to query. This need not represent a * standalone WIM (e.g. it could represent part of a split WIM). * @param buf_ret * On success, a pointer to an allocated buffer containing the raw UTF16-LE * XML document is written to this location. * @param bufsize_ret * The size of the XML document in bytes is written to this location. * * @return 0 on success; a ::wimlib_error_code value on failure. * * @retval ::WIMLIB_ERR_NO_FILENAME * @p wim is not backed by a file and therefore does not have an XML * document. * @retval ::WIMLIB_ERR_READ * Failed to read the XML document from the WIM file. * @retval ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE * Failed to read the XML document from the WIM file. */ extern int wimlib_get_xml_data(WIMStruct *wim, void **buf_ret, size_t *bufsize_ret); /** * @ingroup G_general * * Initialization function for wimlib. Call before using any other wimlib * function (except possibly wimlib_set_print_errors()). If not done manually, * this function will be called automatically with a flags argument of 0. This * function does nothing if called again after it has already successfully run. * * @param init_flags * Bitwise OR of flags prefixed with WIMLIB_INIT_FLAG. * * @return 0 on success; a ::wimlib_error_code value on failure. * * @retval ::WIMLIB_ERR_INSUFFICIENT_PRIVILEGES * ::WIMLIB_INIT_FLAG_STRICT_APPLY_PRIVILEGES and/or * ::WIMLIB_INIT_FLAG_STRICT_CAPTURE_PRIVILEGES were specified in @p * init_flags, but the corresponding privileges could not be acquired. */ extern int wimlib_global_init(int init_flags); /** * @ingroup G_general * * Cleanup function for wimlib. You are not required to call this function, but * it will release any global resources allocated by the library. */ extern void wimlib_global_cleanup(void); /** * @ingroup G_wim_information * * Determine if an image name is already used by some image in the WIM. * * @param wim * Pointer to the ::WIMStruct to query. This need not represent a * standalone WIM (e.g. it could represent part of a split WIM). * @param name * The name to check. * * @return * @c true if there is already an image in @p wim named @p name; @c false * if there is no image named @p name in @p wim. If @p name is @c NULL or * the empty string, then @c false is returned. */ extern bool wimlib_image_name_in_use(const WIMStruct *wim, const wimlib_tchar *name); /** * @ingroup G_wim_information * * Iterate through a file or directory tree in a WIM image. By specifying * appropriate flags and a callback function, you can get the attributes of a * file in the image, get a directory listing, or even get a listing of the * entire image. * * @param wim * The ::WIMStruct containing the image(s) over which to iterate. This * ::WIMStruct must contain image metadata, so it cannot be the non-first * part of a split WIM (for example). * @param image * The 1-based index of the image that contains the files or directories to * iterate over, or ::WIMLIB_ALL_IMAGES to iterate over all images. * @param path * Path in the image at which to do the iteration. * @param flags * Bitwise OR of flags prefixed with WIMLIB_ITERATE_DIR_TREE_FLAG. * @param cb * A callback function that will receive each directory entry. * @param user_ctx * An extra parameter that will always be passed to the callback function * @p cb. * * @return Normally, returns 0 if all calls to @p cb returned 0; otherwise the * first nonzero value that was returned from @p cb. However, additional * ::wimlib_error_code values may be returned, including the following: * * @retval ::WIMLIB_ERR_INVALID_IMAGE * @p image does not exist in @p wim. * @retval ::WIMLIB_ERR_PATH_DOES_NOT_EXIST * @p path does not exist in the image. * @retval ::WIMLIB_ERR_RESOURCE_NOT_FOUND * ::WIMLIB_ITERATE_DIR_TREE_FLAG_RESOURCES_NEEDED was specified, but the * data for some files could not be found in the blob lookup table of @p * wim. * * This function can additionally return ::WIMLIB_ERR_DECOMPRESSION, * ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_METADATA_NOT_FOUND, * ::WIMLIB_ERR_READ, or ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE, all of which * indicate failure (for different reasons) to read the metadata resource for an * image over which iteration needed to be done. */ extern int wimlib_iterate_dir_tree(WIMStruct *wim, int image, const wimlib_tchar *path, int flags, wimlib_iterate_dir_tree_callback_t cb, void *user_ctx); /** * @ingroup G_wim_information * * Iterate through the blob lookup table of a ::WIMStruct. This can be used to * directly get a listing of the unique "blobs" contained in a WIM file, which * are deduplicated over all images. * * Specifically, each listed blob may be from any of the following sources: * * - Metadata blobs, if the ::WIMStruct contains image metadata * - File blobs from the on-disk WIM file (if any) backing the ::WIMStruct * - File blobs from files that have been added to the in-memory ::WIMStruct, * e.g. by using wimlib_add_image() * - File blobs from external WIMs referenced by * wimlib_reference_resource_files() or wimlib_reference_resources() * * @param wim * Pointer to the ::WIMStruct for which to get the blob listing. * @param flags * Reserved; set to 0. * @param cb * A callback function that will receive each blob. * @param user_ctx * An extra parameter that will always be passed to the callback function * @p cb. * * @return 0 if all calls to @p cb returned 0; otherwise the first nonzero value * that was returned from @p cb. */ extern int wimlib_iterate_lookup_table(WIMStruct *wim, int flags, wimlib_iterate_lookup_table_callback_t cb, void *user_ctx); /** * @ingroup G_nonstandalone_wims * * Join a split WIM into a stand-alone (one-part) WIM. * * @param swms * An array of strings that gives the filenames of all parts of the split * WIM. No specific order is required, but all parts must be included with * no duplicates. * @param num_swms * Number of filenames in @p swms. * @param swm_open_flags * Open flags for the split WIM parts (e.g. * ::WIMLIB_OPEN_FLAG_CHECK_INTEGRITY). * @param wim_write_flags * Bitwise OR of relevant flags prefixed with WIMLIB_WRITE_FLAG, which will * be used to write the joined WIM. * @param output_path * The path to write the joined WIM file to. * * @return 0 on success; a ::wimlib_error_code value on failure. This function * may return most error codes that can be returned by wimlib_open_wim() and * wimlib_write(), as well as the following error codes: * * @retval ::WIMLIB_ERR_SPLIT_INVALID * The split WIMs do not form a valid WIM because they do not include all * the parts of the original WIM, there are duplicate parts, or not all the * parts have the same GUID and compression type. * * Note: wimlib is generalized enough that this function is not actually needed * to join a split WIM; instead, you could open the first part of the split WIM, * then reference the other parts with wimlib_reference_resource_files(), then * write the joined WIM using wimlib_write(). However, wimlib_join() provides * an easy-to-use wrapper around this that has some advantages (e.g. extra * sanity checks). */ extern int wimlib_join(const wimlib_tchar * const *swms, unsigned num_swms, const wimlib_tchar *output_path, int swm_open_flags, int wim_write_flags); /** * @ingroup G_nonstandalone_wims * * Same as wimlib_join(), but allows specifying a progress function. The * progress function will receive the write progress messages, such as * ::WIMLIB_PROGRESS_MSG_WRITE_STREAMS, while writing the joined WIM. In * addition, if ::WIMLIB_OPEN_FLAG_CHECK_INTEGRITY is specified in @p * swm_open_flags, the progress function will receive a series of * ::WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY messages when each of the split WIM * parts is opened. */ extern int wimlib_join_with_progress(const wimlib_tchar * const *swms, unsigned num_swms, const wimlib_tchar *output_path, int swm_open_flags, int wim_write_flags, wimlib_progress_func_t progfunc, void *progctx); /** * @ingroup G_mounting_wim_images * * Mount an image from a WIM file on a directory read-only or read-write. * * @param wim * Pointer to the ::WIMStruct containing the image to be mounted. This * ::WIMStruct must have a backing file. * @param image * The 1-based index of the image to mount. This image cannot have been * previously modified in memory. * @param dir * The path to an existing empty directory on which to mount the image. * @param mount_flags * Bitwise OR of flags prefixed with WIMLIB_MOUNT_FLAG. Use * ::WIMLIB_MOUNT_FLAG_READWRITE to request a read-write mount instead of a * read-only mount. * @param staging_dir * If non-NULL, the name of a directory in which a temporary directory for * storing modified or added files will be created. Ignored if * ::WIMLIB_MOUNT_FLAG_READWRITE is not specified in @p mount_flags. If * left @c NULL, the staging directory is created in the same directory as * the backing WIM file. The staging directory is automatically deleted * when the image is unmounted. * * @return 0 on success; a ::wimlib_error_code value on failure. * * @retval ::WIMLIB_ERR_ALREADY_LOCKED * Another process is currently modifying the WIM file. * @retval ::WIMLIB_ERR_FUSE * A non-zero status code was returned by @c fuse_main(). * @retval ::WIMLIB_ERR_IMAGE_HAS_MULTIPLE_REFERENCES * There are currently multiple references to the image as a result of a * call to wimlib_export_image(). Free one before attempting the * read-write mount. * @retval ::WIMLIB_ERR_INVALID_IMAGE * @p image does not exist in @p wim. * @retval ::WIMLIB_ERR_INVALID_PARAM * @p wim was @c NULL; or @p dir was NULL or an empty string; or an * unrecognized flag was specified in @p mount_flags; or the image has * already been modified in memory (e.g. by wimlib_update_image()). * @retval ::WIMLIB_ERR_MKDIR * ::WIMLIB_MOUNT_FLAG_READWRITE was specified in @p mount_flags, but the * staging directory could not be created. * @retval ::WIMLIB_ERR_WIM_IS_READONLY * ::WIMLIB_MOUNT_FLAG_READWRITE was specified in @p mount_flags, but the * WIM file is considered read-only because of any of the reasons mentioned * in the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS flag. * @retval ::WIMLIB_ERR_UNSUPPORTED * Mounting is not supported in this build of the library. * * This function can additionally return ::WIMLIB_ERR_DECOMPRESSION, * ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_METADATA_NOT_FOUND, * ::WIMLIB_ERR_READ, or ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE, all of which * indicate failure (for different reasons) to read the metadata resource for * the image to mount. * * The ability to mount WIM images is implemented using FUSE (Filesystem in * UserSpacE). Depending on how FUSE is set up on your system, this function * may work as normal users in addition to the root user. * * Mounting WIM images is not supported if wimlib was configured * --without-fuse. This includes Windows builds of wimlib; * ::WIMLIB_ERR_UNSUPPORTED will be returned in such cases. * * Calling this function daemonizes the process, unless * ::WIMLIB_MOUNT_FLAG_DEBUG was specified or an early error occurs. * * It is safe to mount multiple images from the same WIM file read-only at the * same time, but only if different ::WIMStruct's are used. It is @b not safe * to mount multiple images from the same WIM file read-write at the same time. * * To unmount the image, call wimlib_unmount_image(). This may be done in a * different process. */ extern int wimlib_mount_image(WIMStruct *wim, int image, const wimlib_tchar *dir, int mount_flags, const wimlib_tchar *staging_dir); /** * @ingroup G_creating_and_opening_wims * * Open a WIM file and create a ::WIMStruct for it. * * @param wim_file * The path to the WIM file to open. * @param open_flags * Bitwise OR of flags prefixed with WIMLIB_OPEN_FLAG. * @param wim_ret * On success, a pointer to a new ::WIMStruct backed by the specified * on-disk WIM file is written to the memory location pointed to by this * parameter. This ::WIMStruct must be freed using using wimlib_free() * when finished with it. * * @return 0 on success; a ::wimlib_error_code value on failure. * * @retval ::WIMLIB_ERR_IMAGE_COUNT * The number of metadata resources found in the WIM did not match the * image count specified in the WIM header, or the number of <IMAGE> * elements in the XML data of the WIM did not match the image count * specified in the WIM header. * @retval ::WIMLIB_ERR_INTEGRITY * ::WIMLIB_OPEN_FLAG_CHECK_INTEGRITY was specified in @p open_flags, and * the WIM file failed the integrity check. * @retval ::WIMLIB_ERR_INVALID_CHUNK_SIZE * The library did not recognize the compression chunk size of the WIM as * valid for its compression type. * @retval ::WIMLIB_ERR_INVALID_COMPRESSION_TYPE * The library did not recognize the compression type of the WIM. * @retval ::WIMLIB_ERR_INVALID_HEADER * The header of the WIM was otherwise invalid. * @retval ::WIMLIB_ERR_INVALID_INTEGRITY_TABLE * ::WIMLIB_OPEN_FLAG_CHECK_INTEGRITY was specified in @p open_flags and * the WIM contained an integrity table, but the integrity table was * invalid. * @retval ::WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY * The lookup table of the WIM was invalid. * @retval ::WIMLIB_ERR_INVALID_PARAM * @p wim_ret was @c NULL; or, @p wim_file was not a nonempty string. * @retval ::WIMLIB_ERR_IS_SPLIT_WIM * The WIM was a split WIM and ::WIMLIB_OPEN_FLAG_ERROR_IF_SPLIT was * specified in @p open_flags. * @retval ::WIMLIB_ERR_NOT_A_WIM_FILE * The file did not begin with the magic characters that identify a WIM * file. * @retval ::WIMLIB_ERR_OPEN * Failed to open the WIM file for reading. Some possible reasons: the WIM * file does not exist, or the calling process does not have permission to * open it. * @retval ::WIMLIB_ERR_READ * Failed to read data from the WIM file. * @retval ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE * Unexpected end-of-file while reading data from the WIM file. * @retval ::WIMLIB_ERR_UNKNOWN_VERSION * The WIM version number was not recognized. (May be a pre-Vista WIM.) * @retval ::WIMLIB_ERR_WIM_IS_ENCRYPTED * The WIM cannot be opened because it contains encrypted segments. (It * may be a Windows 8 "ESD" file.) * @retval ::WIMLIB_ERR_WIM_IS_INCOMPLETE * The WIM file is not complete (e.g. the program which wrote it was * terminated before it finished) * @retval ::WIMLIB_ERR_WIM_IS_READONLY * ::WIMLIB_OPEN_FLAG_WRITE_ACCESS was specified but the WIM file was * considered read-only because of any of the reasons mentioned in the * documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS flag. * @retval ::WIMLIB_ERR_XML * The XML data of the WIM was invalid. */ extern int wimlib_open_wim(const wimlib_tchar *wim_file, int open_flags, WIMStruct **wim_ret); /** * @ingroup G_creating_and_opening_wims * * Same as wimlib_open_wim(), but allows specifying a progress function and * progress context. If successful, the progress function will be registered in * the newly open ::WIMStruct, as if by an automatic call to * wimlib_register_progress_function(). In addition, if * ::WIMLIB_OPEN_FLAG_CHECK_INTEGRITY is specified in @p open_flags, then the * progress function will receive ::WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY * messages while checking the WIM file's integrity. */ extern int wimlib_open_wim_with_progress(const wimlib_tchar *wim_file, int open_flags, WIMStruct **wim_ret, wimlib_progress_func_t progfunc, void *progctx); /** * @ingroup G_writing_and_overwriting_wims * * Commit a ::WIMStruct to disk, updating its backing file. * * There are several alternative ways in which changes may be committed: * * 1. Full rebuild: write the updated WIM to a temporary file, then rename the * temporary file to the original. * 2. Appending: append updates to the new original WIM file, then overwrite * its header such that those changes become visible to new readers. * 3. Compaction: normally should not be used; see * ::WIMLIB_WRITE_FLAG_UNSAFE_COMPACT for details. * * Append mode is often much faster than a full rebuild, but it wastes some * amount of space due to leaving "holes" in the WIM file. Because of the * greater efficiency, wimlib_overwrite() normally defaults to append mode. * However, ::WIMLIB_WRITE_FLAG_REBUILD can be used to explicitly request a full * rebuild. In addition, if wimlib_delete_image() has been used on the * ::WIMStruct, then the default mode switches to rebuild mode, and * ::WIMLIB_WRITE_FLAG_SOFT_DELETE can be used to explicitly request append * mode. * * If this function completes successfully, then no more functions can be called * on the ::WIMStruct other than wimlib_free(). If you need to continue using * the WIM file, you must use wimlib_open_wim() to open a new ::WIMStruct for * it. * * @param wim * Pointer to a ::WIMStruct to commit to its backing file. * @param write_flags * Bitwise OR of relevant flags prefixed with WIMLIB_WRITE_FLAG. * @param num_threads * The number of threads to use for compressing data, or 0 to have the * library automatically choose an appropriate number. * * @return 0 on success; a ::wimlib_error_code value on failure. This function * may return most error codes returned by wimlib_write() as well as the * following error codes: * * @retval ::WIMLIB_ERR_ALREADY_LOCKED * Another process is currently modifying the WIM file. * @retval ::WIMLIB_ERR_NO_FILENAME * @p wim is not backed by an on-disk file. In other words, it is a * ::WIMStruct created by wimlib_create_new_wim() rather than * wimlib_open_wim(). * @retval ::WIMLIB_ERR_RENAME * The temporary file to which the WIM was written could not be renamed to * the original file. * @retval ::WIMLIB_ERR_WIM_IS_READONLY * The WIM file is considered read-only because of any of the reasons * mentioned in the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS * flag. * * If a progress function is registered with @p wim, then it will receive the * messages ::WIMLIB_PROGRESS_MSG_WRITE_STREAMS, * ::WIMLIB_PROGRESS_MSG_WRITE_METADATA_BEGIN, and * ::WIMLIB_PROGRESS_MSG_WRITE_METADATA_END. */ extern int wimlib_overwrite(WIMStruct *wim, int write_flags, unsigned num_threads); /** * @ingroup G_wim_information * * (Deprecated) Print information about one image, or all images, contained in a * WIM. * * @param wim * Pointer to the ::WIMStruct to query. This need not represent a * standalone WIM (e.g. it could represent part of a split WIM). * @param image * The 1-based index of the image for which to print information, or * ::WIMLIB_ALL_IMAGES to print information about all images. * * @return This function has no return value. No error checking is done when * printing the information. If @p image is invalid, an error message is * printed. * * This function is deprecated; use wimlib_get_xml_data() or * wimlib_get_image_property() to query image information instead. */ extern void wimlib_print_available_images(const WIMStruct *wim, int image); /** * @ingroup G_wim_information * * Print the header of the WIM file (intended for debugging only). */ extern void wimlib_print_header(const WIMStruct *wim); /** * @ingroup G_nonstandalone_wims * * Reference file data from other WIM files or split WIM parts. This function * can be used on WIMs that are not standalone, such as split or "delta" WIMs, * to load additional file data before calling a function such as * wimlib_extract_image() that requires the file data to be present. * * @param wim * The ::WIMStruct for a WIM that contains metadata resources, but is not * necessarily "standalone". In the case of split WIMs, this should be the * first part, since only the first part contains the metadata resources. * In the case of delta WIMs, this should be the delta WIM rather than the * WIM on which it is based. * @param resource_wimfiles_or_globs * Array of paths to WIM files and/or split WIM parts to reference. * Alternatively, when ::WIMLIB_REF_FLAG_GLOB_ENABLE is specified in @p * ref_flags, these are treated as globs rather than literal paths. That * is, using this function you can specify zero or more globs, each of * which expands to one or more literal paths. * @param count * Number of entries in @p resource_wimfiles_or_globs. * @param ref_flags * Bitwise OR of ::WIMLIB_REF_FLAG_GLOB_ENABLE and/or * ::WIMLIB_REF_FLAG_GLOB_ERR_ON_NOMATCH. * @param open_flags * Additional open flags, such as ::WIMLIB_OPEN_FLAG_CHECK_INTEGRITY, to * pass to internal calls to wimlib_open_wim() on the reference files. * * @return 0 on success; a ::wimlib_error_code value on failure. * * @retval ::WIMLIB_ERR_GLOB_HAD_NO_MATCHES * One of the specified globs did not match any paths (only with both * ::WIMLIB_REF_FLAG_GLOB_ENABLE and ::WIMLIB_REF_FLAG_GLOB_ERR_ON_NOMATCH * specified in @p ref_flags). * @retval ::WIMLIB_ERR_READ * I/O or permissions error while processing a file glob. * * This function can additionally return most values that can be returned by * wimlib_open_wim(). */ extern int wimlib_reference_resource_files(WIMStruct *wim, const wimlib_tchar * const *resource_wimfiles_or_globs, unsigned count, int ref_flags, int open_flags); /** * @ingroup G_nonstandalone_wims * * Similar to wimlib_reference_resource_files(), but operates at a lower level * where the caller must open the ::WIMStruct for each referenced file itself. * * @param wim * The ::WIMStruct for a WIM that contains metadata resources, but is not * necessarily "standalone". In the case of split WIMs, this should be the * first part, since only the first part contains the metadata resources. * @param resource_wims * Array of pointers to the ::WIMStruct's for additional resource WIMs or * split WIM parts to reference. * @param num_resource_wims * Number of entries in @p resource_wims. * @param ref_flags * Reserved; must be 0. * * @return 0 on success; a ::wimlib_error_code value on failure. */ extern int wimlib_reference_resources(WIMStruct *wim, WIMStruct **resource_wims, unsigned num_resource_wims, int ref_flags); /** * @ingroup G_modifying_wims * * Declare that a newly added image is mostly the same as a prior image, but * captured at a later point in time, possibly with some modifications in the * intervening time. This is designed to be used in incremental backups of the * same filesystem or directory tree. * * This function compares the metadata of the directory tree of the newly added * image against that of the old image. Any files that are present in both the * newly added image and the old image and have timestamps that indicate they * haven't been modified are deemed not to have been modified and have their * checksums copied from the old image. Because of this and because WIM uses * single-instance streams, such files need not be read from the filesystem when * the WIM is being written or overwritten. Note that these unchanged files * will still be "archived" and will be logically present in the new image; the * optimization is that they don't need to actually be read from the filesystem * because the WIM already contains them. * * This function is provided to optimize incremental backups. The resulting WIM * file will still be the same regardless of whether this function is called. * (This is, however, assuming that timestamps have not been manipulated or * unmaintained as to trick this function into thinking a file has not been * modified when really it has. To partly guard against such cases, other * metadata such as file sizes will be checked as well.) * * This function must be called after adding the new image (e.g. with * wimlib_add_image()), but before writing the updated WIM file (e.g. with * wimlib_overwrite()). * * @param wim * Pointer to the ::WIMStruct containing the newly added image. * @param new_image * The 1-based index in @p wim of the newly added image. * @param template_wim * Pointer to the ::WIMStruct containing the template image. This can be, * but does not have to be, the same ::WIMStruct as @p wim. * @param template_image * The 1-based index in @p template_wim of the template image. * @param flags * Reserved; must be 0. * * @return 0 on success; a ::wimlib_error_code value on failure. * * @retval ::WIMLIB_ERR_INVALID_IMAGE * @p new_image does not exist in @p wim or @p template_image does not * exist in @p template_wim. * @retval ::WIMLIB_ERR_METADATA_NOT_FOUND * At least one of @p wim and @p template_wim does not contain image * metadata; for example, one of them represents a non-first part of a * split WIM. * @retval ::WIMLIB_ERR_INVALID_PARAM * Identical values were provided for the template and new image; or @p * new_image specified an image that had not been modified since opening * the WIM. * * This function can additionally return ::WIMLIB_ERR_DECOMPRESSION, * ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_METADATA_NOT_FOUND, * ::WIMLIB_ERR_READ, or ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE, all of which * indicate failure (for different reasons) to read the metadata resource for * the template image. */ extern int wimlib_reference_template_image(WIMStruct *wim, int new_image, WIMStruct *template_wim, int template_image, int flags); /** * @ingroup G_general * * Register a progress function with a ::WIMStruct. * * @param wim * The ::WIMStruct for which to register the progress function. * @param progfunc * Pointer to the progress function to register. If the WIM already has a * progress function registered, it will be replaced with this one. If @p * NULL, the current progress function (if any) will be unregistered. * @param progctx * The value which will be passed as the third argument to calls to @p * progfunc. */ extern void wimlib_register_progress_function(WIMStruct *wim, wimlib_progress_func_t progfunc, void *progctx); /** * @ingroup G_modifying_wims * * Rename the @p source_path to the @p dest_path in the specified @p image of * the @p wim. * * This just builds an appropriate ::wimlib_rename_command and passes it to * wimlib_update_image(). */ extern int wimlib_rename_path(WIMStruct *wim, int image, const wimlib_tchar *source_path, const wimlib_tchar *dest_path); /** * @ingroup G_wim_information * * Translate a string specifying the name or number of an image in the WIM into * the number of the image. The images are numbered starting at 1. * * @param wim * Pointer to the ::WIMStruct for a WIM. * @param image_name_or_num * A string specifying the name or number of an image in the WIM. If it * parses to a positive integer, this integer is taken to specify the * number of the image, indexed starting at 1. Otherwise, it is taken to * be the name of an image, as given in the XML data for the WIM file. It * also may be the keyword "all" or the string "*", both of which will * resolve to ::WIMLIB_ALL_IMAGES. *

* There is no way to search for an image actually named "all", "*", or an * integer number, or an image that has no name. However, you can use * wimlib_get_image_name() to get the name of any image. * * @return * If the string resolved to a single existing image, the number of that * image, indexed starting at 1, is returned. If the keyword "all" or "*" * was specified, ::WIMLIB_ALL_IMAGES is returned. Otherwise, * ::WIMLIB_NO_IMAGE is returned. If @p image_name_or_num was @c NULL or * the empty string, ::WIMLIB_NO_IMAGE is returned, even if one or more * images in @p wim has no name. (Since a WIM may have multiple unnamed * images, an unnamed image must be specified by index to eliminate the * ambiguity.) */ extern int wimlib_resolve_image(WIMStruct *wim, const wimlib_tchar *image_name_or_num); /** * @ingroup G_general * * Set the file to which the library will print error and warning messages. * * This version of the function takes a C library FILE* opened for * writing (or appending). Use wimlib_set_error_file_by_name() to specify the * file by name instead. * * This also enables error messages, as if by a call to * wimlib_set_print_errors(true). * * @return 0 on success; a ::wimlib_error_code value on failure. * * @retval ::WIMLIB_ERR_UNSUPPORTED * wimlib was compiled using the --without-error-messages option. */ extern int wimlib_set_error_file(FILE *fp); /** * @ingroup G_general * * Set the path to the file to which the library will print error and warning * messages. The library will open this file for appending. * * This also enables error messages, as if by a call to * wimlib_set_print_errors(true). * * @return 0 on success; a ::wimlib_error_code value on failure. * * @retval ::WIMLIB_ERR_OPEN * The file named by @p path could not be opened for appending. * @retval ::WIMLIB_ERR_UNSUPPORTED * wimlib was compiled using the --without-error-messages option. */ extern int wimlib_set_error_file_by_name(const wimlib_tchar *path); /** * @ingroup G_modifying_wims * * Change the description of a WIM image. Equivalent to * wimlib_set_image_property(wim, image, "DESCRIPTION", description). * * Note that "description" is misspelled in the name of this function. */ extern int wimlib_set_image_descripton(WIMStruct *wim, int image, const wimlib_tchar *description); /** * @ingroup G_modifying_wims * * Change what is stored in the \ element in the WIM XML document * (usually something like "Core" or "Ultimate"). Equivalent to * wimlib_set_image_property(wim, image, "FLAGS", flags). */ extern int wimlib_set_image_flags(WIMStruct *wim, int image, const wimlib_tchar *flags); /** * @ingroup G_modifying_wims * * Change the name of a WIM image. Equivalent to * wimlib_set_image_property(wim, image, "NAME", name). */ extern int wimlib_set_image_name(WIMStruct *wim, int image, const wimlib_tchar *name); /** * @ingroup G_modifying_wims * * Since wimlib v1.8.3: add, modify, or remove a per-image property from the * WIM's XML document. This is an alternative to wimlib_set_image_name(), * wimlib_set_image_descripton(), and wimlib_set_image_flags() which allows * manipulating any simple string property. * * @param wim * Pointer to the ::WIMStruct for the WIM. * @param image * The 1-based index of the image for which to set the property. * @param property_name * The name of the image property in the same format documented for * wimlib_get_image_property(). *
* Note: if creating a new element using a bracketed index such as * "WINDOWS/LANGUAGES/LANGUAGE[2]", the highest index that can be specified * is one greater than the number of existing elements with that same name, * excluding the index. That means that if you are adding a list of new * elements, they must be added sequentially from the first index (1) to * the last index (n). * @param property_value * If not NULL and not empty, the property is set to this value. * Otherwise, the property is removed from the XML document. * * @return 0 on success; a ::wimlib_error_code value on failure. * * @retval ::WIMLIB_ERR_IMAGE_NAME_COLLISION * The user requested to set the image name (the NAME property), * but another image in the WIM already had the requested name. * @retval ::WIMLIB_ERR_INVALID_IMAGE * @p image does not exist in @p wim. * @retval ::WIMLIB_ERR_INVALID_PARAM * @p property_name has an unsupported format, or @p property_name included * a bracketed index that was too high. */ extern int wimlib_set_image_property(WIMStruct *wim, int image, const wimlib_tchar *property_name, const wimlib_tchar *property_value); /** * @ingroup G_general * * Set the functions that wimlib uses to allocate and free memory. * * These settings are global and not per-WIM. * * The default is to use the default @c malloc(), @c free(), and @c realloc() * from the standard C library. * * Note: some external functions, such as those in @c libntfs-3g, may use the * standard memory allocation functions regardless of this setting. * * @param malloc_func * A function equivalent to @c malloc() that wimlib will use to allocate * memory. If @c NULL, the allocator function is set back to the default * @c malloc() from the C library. * @param free_func * A function equivalent to @c free() that wimlib will use to free memory. * If @c NULL, the free function is set back to the default @c free() from * the C library. * @param realloc_func * A function equivalent to @c realloc() that wimlib will use to reallocate * memory. If @c NULL, the free function is set back to the default @c * realloc() from the C library. * * @return 0 */ extern int wimlib_set_memory_allocator(void *(*malloc_func)(size_t), void (*free_func)(void *), void *(*realloc_func)(void *, size_t)); /** * @ingroup G_writing_and_overwriting_wims * * Set a ::WIMStruct's output compression chunk size. This is the compression * chunk size that will be used for writing non-solid resources in subsequent * calls to wimlib_write() or wimlib_overwrite(). A larger compression chunk * size often results in a better compression ratio, but compression may be * slower and the speed of random access to data may be reduced. In addition, * some chunk sizes are not compatible with Microsoft software. * * @param wim * The ::WIMStruct for which to set the output chunk size. * @param chunk_size * The chunk size (in bytes) to set. The valid chunk sizes are dependent * on the compression type. See the documentation for each * ::wimlib_compression_type constant for more information. As a special * case, if @p chunk_size is specified as 0, then the chunk size will be * reset to the default for the currently selected output compression type. * * @return 0 on success; a ::wimlib_error_code value on failure. * * @retval ::WIMLIB_ERR_INVALID_CHUNK_SIZE * @p chunk_size was not 0 or a supported chunk size for the currently * selected output compression type. */ extern int wimlib_set_output_chunk_size(WIMStruct *wim, uint32_t chunk_size); /** * @ingroup G_writing_and_overwriting_wims * * Similar to wimlib_set_output_chunk_size(), but set the chunk size for writing * solid resources. */ extern int wimlib_set_output_pack_chunk_size(WIMStruct *wim, uint32_t chunk_size); /** * @ingroup G_writing_and_overwriting_wims * * Set a ::WIMStruct's output compression type. This is the compression type * that will be used for writing non-solid resources in subsequent calls to * wimlib_write() or wimlib_overwrite(). * * @param wim * The ::WIMStruct for which to set the output compression type. * @param ctype * The compression type to set. If this compression type is incompatible * with the current output chunk size, then the output chunk size will be * reset to the default for the new compression type. * * @return 0 on success; a ::wimlib_error_code value on failure. * * @retval ::WIMLIB_ERR_INVALID_COMPRESSION_TYPE * @p ctype did not specify a valid compression type. */ extern int wimlib_set_output_compression_type(WIMStruct *wim, enum wimlib_compression_type ctype); /** * @ingroup G_writing_and_overwriting_wims * * Similar to wimlib_set_output_compression_type(), but set the compression type * for writing solid resources. This cannot be ::WIMLIB_COMPRESSION_TYPE_NONE. */ extern int wimlib_set_output_pack_compression_type(WIMStruct *wim, enum wimlib_compression_type ctype); /** * @ingroup G_general * * Set whether wimlib can print error and warning messages to the error file, * which defaults to standard error. Error and warning messages may provide * information that cannot be determined only from returned error codes. * * By default, error messages are not printed. * * This setting applies globally (it is not per-WIM). * * This can be called before wimlib_global_init(). * * @param show_messages * @c true if messages are to be printed; @c false if messages are not to * be printed. * * @return 0 on success; a ::wimlib_error_code value on failure. * * @retval ::WIMLIB_ERR_UNSUPPORTED * wimlib was compiled using the --without-error-messages option. */ extern int wimlib_set_print_errors(bool show_messages); /** * @ingroup G_modifying_wims * * Set basic information about a WIM. * * @param wim * Pointer to the ::WIMStruct for a WIM. * @param info * Pointer to a ::wimlib_wim_info structure that contains the information * to set. Only the information explicitly specified in the @p which flags * need be valid. * @param which * Flags that specify which information to set. This is a bitwise OR of * ::WIMLIB_CHANGE_READONLY_FLAG, ::WIMLIB_CHANGE_GUID, * ::WIMLIB_CHANGE_BOOT_INDEX, and/or ::WIMLIB_CHANGE_RPFIX_FLAG. * * @return 0 on success; a ::wimlib_error_code value on failure. * * @retval ::WIMLIB_ERR_IMAGE_COUNT * ::WIMLIB_CHANGE_BOOT_INDEX was specified, but * ::wimlib_wim_info.boot_index did not specify 0 or a valid 1-based image * index in the WIM. */ extern int wimlib_set_wim_info(WIMStruct *wim, const struct wimlib_wim_info *info, int which); /** * @ingroup G_nonstandalone_wims * * Split a WIM into multiple parts. * * @param wim * The ::WIMStruct for the WIM to split. * @param swm_name * Name of the split WIM (SWM) file to create. This will be the name of * the first part. The other parts will, by default, have the same name * with 2, 3, 4, ..., etc. appended before the suffix. However, the exact * names can be customized using the progress function. * @param part_size * The maximum size per part, in bytes. Unfortunately, it is not * guaranteed that this will really be the maximum size per part, because * some file resources in the WIM may be larger than this size, and the WIM * file format provides no way to split up file resources among multiple * WIMs. * @param write_flags * Bitwise OR of relevant flags prefixed with @c WIMLIB_WRITE_FLAG. These * flags will be used to write each split WIM part. Specify 0 here to get * the default behavior. * * @return 0 on success; a ::wimlib_error_code value on failure. This function * may return most error codes that can be returned by wimlib_write() as well as * the following error codes: * * @retval ::WIMLIB_ERR_INVALID_PARAM * @p swm_name was not a nonempty string, or @p part_size was 0. * @retval ::WIMLIB_ERR_UNSUPPORTED * The WIM contains solid resources. Splitting a WIM containing solid * resources is not supported. * * If a progress function is registered with @p wim, then for each split WIM * part that is written it will receive the messages * ::WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART and * ::WIMLIB_PROGRESS_MSG_SPLIT_END_PART. */ extern int wimlib_split(WIMStruct *wim, const wimlib_tchar *swm_name, uint64_t part_size, int write_flags); /** * @ingroup G_general * * Perform verification checks on a WIM file. * * This function is intended for safety checking and/or debugging. If used on a * well-formed WIM file, it should always succeed. * * @param wim * The ::WIMStruct for the WIM file to verify. Note: for an extra layer of * verification, it is a good idea to have used * ::WIMLIB_OPEN_FLAG_CHECK_INTEGRITY when you opened the file. *
* If verifying a split WIM, specify the first part of the split WIM here, * and reference the other parts using wimlib_reference_resource_files() * before calling this function. * @param verify_flags * Reserved; must be 0. * * @return 0 if the WIM file was successfully verified; a ::wimlib_error_code * value if it failed verification or another error occurred. * * @retval ::WIMLIB_ERR_DECOMPRESSION * The WIM file contains invalid compressed data. * @retval ::WIMLIB_ERR_INVALID_METADATA_RESOURCE * The metadata resource for an image is invalid. * @retval ::WIMLIB_ERR_INVALID_RESOURCE_HASH * File data stored in the WIM file is corrupt. * @retval ::WIMLIB_ERR_RESOURCE_NOT_FOUND * The data for a file in an image could not be found. See @ref * G_nonstandalone_wims. * * If a progress function is registered with @p wim, then it will receive the * following progress messages: ::WIMLIB_PROGRESS_MSG_BEGIN_VERIFY_IMAGE, * ::WIMLIB_PROGRESS_MSG_END_VERIFY_IMAGE, and * ::WIMLIB_PROGRESS_MSG_VERIFY_STREAMS. */ extern int wimlib_verify_wim(WIMStruct *wim, int verify_flags); /** * @ingroup G_mounting_wim_images * * Unmount a WIM image that was mounted using wimlib_mount_image(). * * When unmounting a read-write mounted image, the default behavior is to * discard changes to the image. Use ::WIMLIB_UNMOUNT_FLAG_COMMIT to cause the * image to be committed. * * @param dir * The directory on which the WIM image is mounted. * @param unmount_flags * Bitwise OR of flags prefixed with @p WIMLIB_UNMOUNT_FLAG. * * @return 0 on success; a ::wimlib_error_code value on failure. * * @retval ::WIMLIB_ERR_NOT_A_MOUNTPOINT * There is no WIM image mounted on the specified directory. * @retval ::WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY * The read-write mounted image cannot be committed because there are file * descriptors open to it, and ::WIMLIB_UNMOUNT_FLAG_FORCE was not * specified. * @retval ::WIMLIB_ERR_MQUEUE * Could not create a POSIX message queue. * @retval ::WIMLIB_ERR_NOT_PERMITTED_TO_UNMOUNT * The image was mounted by a different user. * @retval ::WIMLIB_ERR_UNSUPPORTED * Mounting is not supported in this build of the library. * * Note: you can also unmount the image by using the @c umount() system call, or * by using the @c umount or @c fusermount programs. However, you need to call * this function if you want changes to be committed. */ extern int wimlib_unmount_image(const wimlib_tchar *dir, int unmount_flags); /** * @ingroup G_mounting_wim_images * * Same as wimlib_unmount_image(), but allows specifying a progress function. * The progress function will receive a ::WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN * message. In addition, if changes are committed from a read-write mount, the * progress function will receive ::WIMLIB_PROGRESS_MSG_WRITE_STREAMS messages. */ extern int wimlib_unmount_image_with_progress(const wimlib_tchar *dir, int unmount_flags, wimlib_progress_func_t progfunc, void *progctx); /** * @ingroup G_modifying_wims * * Update a WIM image by adding, deleting, and/or renaming files or directories. * * @param wim * Pointer to the ::WIMStruct containing the image to update. * @param image * The 1-based index of the image to update. * @param cmds * An array of ::wimlib_update_command's that specify the update operations * to perform. * @param num_cmds * Number of commands in @p cmds. * @param update_flags * ::WIMLIB_UPDATE_FLAG_SEND_PROGRESS or 0. * * @return 0 on success; a ::wimlib_error_code value on failure. On failure, * all update commands will be rolled back, and no visible changes will have * been made to @p wim. * * @retval ::WIMLIB_ERR_FVE_LOCKED_VOLUME * Windows-only: One of the "add" commands attempted to add files from an * encrypted BitLocker volume that hasn't yet been unlocked. * @retval ::WIMLIB_ERR_IMAGE_HAS_MULTIPLE_REFERENCES * There are currently multiple references to the image as a result of a * call to wimlib_export_image(). Free one before attempting the update. * @retval ::WIMLIB_ERR_INVALID_CAPTURE_CONFIG * The contents of a capture configuration file were invalid. * @retval ::WIMLIB_ERR_INVALID_IMAGE * @p image did not exist in @p wim. * @retval ::WIMLIB_ERR_INVALID_OVERLAY * An add command with ::WIMLIB_ADD_FLAG_NO_REPLACE specified attempted to * replace an existing nondirectory file. * @retval ::WIMLIB_ERR_INVALID_PARAM * An unknown operation type was provided in the update commands; or * unknown or incompatible flags were provided in a flags parameter; or * there was another problem with the provided parameters. * @retval ::WIMLIB_ERR_INVALID_REPARSE_DATA * While executing an add command, a reparse point had invalid data. * @retval ::WIMLIB_ERR_IS_DIRECTORY * An add command attempted to replace a directory with a non-directory; or * a delete command without ::WIMLIB_DELETE_FLAG_RECURSIVE attempted to * delete a directory; or a rename command attempted to rename a directory * to a non-directory. * @retval ::WIMLIB_ERR_NOTDIR * An add command attempted to replace a non-directory with a directory; or * an add command attempted to set the root of the image to a * non-directory; or a rename command attempted to rename a directory to a * non-directory; or a component of an image path that was used as a * directory was not, in fact, a directory. * @retval ::WIMLIB_ERR_NOTEMPTY * A rename command attempted to rename a directory to a non-empty * directory; or a rename command would have created a loop. * @retval ::WIMLIB_ERR_NTFS_3G * While executing an add command with ::WIMLIB_ADD_FLAG_NTFS specified, an * error occurred while reading data from the NTFS volume using libntfs-3g. * @retval ::WIMLIB_ERR_OPEN * Failed to open a file to be captured while executing an add command. * @retval ::WIMLIB_ERR_OPENDIR * Failed to open a directory to be captured while executing an add * command. * @retval ::WIMLIB_ERR_PATH_DOES_NOT_EXIST * A delete command without ::WIMLIB_DELETE_FLAG_FORCE specified was for a * WIM path that did not exist; or a rename command attempted to rename a * file that does not exist. * @retval ::WIMLIB_ERR_READ * While executing an add command, failed to read data from a file or * directory to be captured. * @retval ::WIMLIB_ERR_READLINK * While executing an add command, failed to read the target of a symbolic * link, junction, or other reparse point. * @retval ::WIMLIB_ERR_STAT * While executing an add command, failed to read metadata for a file or * directory. * @retval ::WIMLIB_ERR_UNABLE_TO_READ_CAPTURE_CONFIG * A capture configuration file could not be read. * @retval ::WIMLIB_ERR_UNSUPPORTED * A command had flags provided that are not supported on this platform or * in this build of the library. * @retval ::WIMLIB_ERR_UNSUPPORTED_FILE * An add command with ::WIMLIB_ADD_FLAG_NO_UNSUPPORTED_EXCLUDE specified * discovered a file that was not of a supported type. * * This function can additionally return ::WIMLIB_ERR_DECOMPRESSION, * ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_METADATA_NOT_FOUND, * ::WIMLIB_ERR_READ, or ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE, all of which * indicate failure (for different reasons) to read the metadata resource for an * image that needed to be updated. */ extern int wimlib_update_image(WIMStruct *wim, int image, const struct wimlib_update_command *cmds, size_t num_cmds, int update_flags); /** * @ingroup G_writing_and_overwriting_wims * * Persist a ::WIMStruct to a new on-disk WIM file. * * This brings in file data from any external locations, such as directory trees * or NTFS volumes scanned with wimlib_add_image(), or other WIM files via * wimlib_export_image(), and incorporates it into a new on-disk WIM file. * * By default, the new WIM file is written as stand-alone. Using the * ::WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS flag, a "delta" WIM can be written * instead. However, this function cannot directly write a "split" WIM; use * wimlib_split() for that. * * @param wim * Pointer to the ::WIMStruct being persisted. * @param path * The path to the on-disk file to write. * @param image * Normally, specify ::WIMLIB_ALL_IMAGES here. This indicates that all * images are to be included in the new on-disk WIM file. If for some * reason you only want to include a single image, specify the 1-based * index of that image instead. * @param write_flags * Bitwise OR of flags prefixed with @c WIMLIB_WRITE_FLAG. * @param num_threads * The number of threads to use for compressing data, or 0 to have the * library automatically choose an appropriate number. * * @return 0 on success; a ::wimlib_error_code value on failure. * * @retval ::WIMLIB_ERR_CONCURRENT_MODIFICATION_DETECTED * A file that had previously been scanned for inclusion in the WIM was * concurrently modified. * @retval ::WIMLIB_ERR_INVALID_IMAGE * @p image did not exist in @p wim. * @retval ::WIMLIB_ERR_INVALID_RESOURCE_HASH * A file, stored in another WIM, which needed to be written was corrupt. * @retval ::WIMLIB_ERR_INVALID_PARAM * @p path was not a nonempty string, or invalid flags were passed. * @retval ::WIMLIB_ERR_OPEN * Failed to open the output WIM file for writing, or failed to open a file * whose data needed to be included in the WIM. * @retval ::WIMLIB_ERR_READ * Failed to read data that needed to be included in the WIM. * @retval ::WIMLIB_ERR_RESOURCE_NOT_FOUND * A file data blob that needed to be written could not be found in the * blob lookup table of @p wim. See @ref G_nonstandalone_wims. * @retval ::WIMLIB_ERR_WRITE * An error occurred when trying to write data to the new WIM file. * * This function can additionally return ::WIMLIB_ERR_DECOMPRESSION, * ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_METADATA_NOT_FOUND, * ::WIMLIB_ERR_READ, or ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE, all of which * indicate failure (for different reasons) to read the data from a WIM file. * * If a progress function is registered with @p wim, then it will receive the * messages ::WIMLIB_PROGRESS_MSG_WRITE_STREAMS, * ::WIMLIB_PROGRESS_MSG_WRITE_METADATA_BEGIN, and * ::WIMLIB_PROGRESS_MSG_WRITE_METADATA_END. */ extern int wimlib_write(WIMStruct *wim, const wimlib_tchar *path, int image, int write_flags, unsigned num_threads); /** * @ingroup G_writing_and_overwriting_wims * * Same as wimlib_write(), but write the WIM directly to a file descriptor, * which need not be seekable if the write is done in a special pipable WIM * format by providing ::WIMLIB_WRITE_FLAG_PIPABLE in @p write_flags. This can, * for example, allow capturing a WIM image and streaming it over the network. * See @ref subsec_pipable_wims for more information about pipable WIMs. * * The file descriptor @p fd will @b not be closed when the write is complete; * the calling code is responsible for this. * * @return 0 on success; a ::wimlib_error_code value on failure. The possible * error codes include those that can be returned by wimlib_write() as well as * the following: * * @retval ::WIMLIB_ERR_INVALID_PARAM * @p fd was not seekable, but ::WIMLIB_WRITE_FLAG_PIPABLE was not * specified in @p write_flags. */ extern int wimlib_write_to_fd(WIMStruct *wim, int fd, int image, int write_flags, unsigned num_threads); /** * @defgroup G_compression Compression and decompression functions * * @brief Functions for XPRESS, LZX, and LZMS compression and decompression. * * These functions are already used by wimlib internally when appropriate for * reading and writing WIM archives. But they are exported and documented so * that they can be used in other applications or libraries for general-purpose * lossless data compression. They are implemented in highly optimized C code, * using state-of-the-art compression techniques. The main limitation is the * lack of sliding window support; this has, however, allowed the algorithms to * be optimized for block-based compression. * * @{ */ /** Opaque compressor handle. */ struct wimlib_compressor; /** Opaque decompressor handle. */ struct wimlib_decompressor; /** * Set the default compression level for the specified compression type. This * is the compression level that wimlib_create_compressor() assumes if it is * called with @p compression_level specified as 0. * * wimlib's WIM writing code (e.g. wimlib_write()) will pass 0 to * wimlib_create_compressor() internally. Therefore, calling this function will * affect the compression level of any data later written to WIM files using the * specified compression type. * * The initial state, before this function is called, is that all compression * types have a default compression level of 50. * * @param ctype * Compression type for which to set the default compression level, as one * of the ::wimlib_compression_type constants. Or, if this is the special * value -1, the default compression levels for all compression types will * be set. * @param compression_level * The default compression level to set. If 0, the "default default" level * of 50 is restored. Otherwise, a higher value indicates higher * compression, whereas a lower value indicates lower compression. See * wimlib_create_compressor() for more information. * * @return 0 on success; a ::wimlib_error_code value on failure. * * @retval ::WIMLIB_ERR_INVALID_COMPRESSION_TYPE * @p ctype was neither a supported compression type nor -1. */ extern int wimlib_set_default_compression_level(int ctype, unsigned int compression_level); /** * Return the approximate number of bytes needed to allocate a compressor with * wimlib_create_compressor() for the specified compression type, maximum block * size, and compression level. @p compression_level may be 0, in which case * the current default compression level for @p ctype is used. Returns 0 if the * compression type is invalid, or the @p max_block_size for that compression * type is invalid. */ extern uint64_t wimlib_get_compressor_needed_memory(enum wimlib_compression_type ctype, size_t max_block_size, unsigned int compression_level); #define WIMLIB_COMPRESSOR_FLAG_DESTRUCTIVE 0x80000000 /** * Allocate a compressor for the specified compression type using the specified * parameters. This function is part of wimlib's compression API; it is not * necessary to call this to process a WIM file. * * @param ctype * Compression type for which to create the compressor, as one of the * ::wimlib_compression_type constants. * @param max_block_size * The maximum compression block size to support. This specifies the * maximum allowed value for the @p uncompressed_size parameter of * wimlib_compress() when called using this compressor. *
* Usually, the amount of memory used by the compressor will scale in * proportion to the @p max_block_size parameter. * wimlib_get_compressor_needed_memory() can be used to query the specific * amount of memory that will be required. *
* This parameter must be at least 1 and must be less than or equal to a * compression-type-specific limit. *
* In general, the same value of @p max_block_size must be passed to * wimlib_create_decompressor() when the data is later decompressed. * However, some compression types have looser requirements regarding this. * @param compression_level * The compression level to use. If 0, the default compression level (50, * or another value as set through wimlib_set_default_compression_level()) * is used. Otherwise, a higher value indicates higher compression. The * values are scaled so that 10 is low compression, 50 is medium * compression, and 100 is high compression. This is not a percentage; * values above 100 are also valid. *
* Using a higher-than-default compression level can result in a better * compression ratio, but can significantly reduce performance. Similarly, * using a lower-than-default compression level can result in better * performance, but can significantly worsen the compression ratio. The * exact results will depend heavily on the compression type and what * algorithms are implemented for it. If you are considering using a * non-default compression level, you should run benchmarks to see if it is * worthwhile for your application. *
* The compression level does not affect the format of the compressed data. * Therefore, it is a compressor-only parameter and does not need to be * passed to the decompressor. *
* Since wimlib v1.8.0, this parameter can be OR-ed with the flag * ::WIMLIB_COMPRESSOR_FLAG_DESTRUCTIVE. This creates the compressor in a * mode where it is allowed to modify the input buffer. Specifically, in * this mode, if compression succeeds, the input buffer may have been * modified, whereas if compression does not succeed the input buffer still * may have been written to but will have been restored exactly to its * original state. This mode is designed to save some memory when using * large buffer sizes. * @param compressor_ret * A location into which to return the pointer to the allocated compressor. * The allocated compressor can be used for any number of calls to * wimlib_compress() before being freed with wimlib_free_compressor(). * * @return 0 on success; a ::wimlib_error_code value on failure. * * @retval ::WIMLIB_ERR_INVALID_COMPRESSION_TYPE * @p ctype was not a supported compression type. * @retval ::WIMLIB_ERR_INVALID_PARAM * @p max_block_size was invalid for the compression type, or @p * compressor_ret was @c NULL. * @retval ::WIMLIB_ERR_NOMEM * Insufficient memory to allocate the compressor. */ extern int wimlib_create_compressor(enum wimlib_compression_type ctype, size_t max_block_size, unsigned int compression_level, struct wimlib_compressor **compressor_ret); /** * Compress a buffer of data. * * @param uncompressed_data * Buffer containing the data to compress. * @param uncompressed_size * Size, in bytes, of the data to compress. This cannot be greater than * the @p max_block_size with which wimlib_create_compressor() was called. * (If it is, the data will not be compressed and 0 will be returned.) * @param compressed_data * Buffer into which to write the compressed data. * @param compressed_size_avail * Number of bytes available in @p compressed_data. * @param compressor * A compressor previously allocated with wimlib_create_compressor(). * * @return * The size of the compressed data, in bytes, or 0 if the data could not be * compressed to @p compressed_size_avail or fewer bytes. */ extern size_t wimlib_compress(const void *uncompressed_data, size_t uncompressed_size, void *compressed_data, size_t compressed_size_avail, struct wimlib_compressor *compressor); /** * Free a compressor previously allocated with wimlib_create_compressor(). * * @param compressor * The compressor to free. If @c NULL, no action is taken. */ extern void wimlib_free_compressor(struct wimlib_compressor *compressor); /** * Allocate a decompressor for the specified compression type. This function is * part of wimlib's compression API; it is not necessary to call this to process * a WIM file. * * @param ctype * Compression type for which to create the decompressor, as one of the * ::wimlib_compression_type constants. * @param max_block_size * The maximum compression block size to support. This specifies the * maximum allowed value for the @p uncompressed_size parameter of * wimlib_decompress(). *
* In general, this parameter must be the same as the @p max_block_size * that was passed to wimlib_create_compressor() when the data was * compressed. However, some compression types have looser requirements * regarding this. * @param decompressor_ret * A location into which to return the pointer to the allocated * decompressor. The allocated decompressor can be used for any number of * calls to wimlib_decompress() before being freed with * wimlib_free_decompressor(). * * @return 0 on success; a ::wimlib_error_code value on failure. * * @retval ::WIMLIB_ERR_INVALID_COMPRESSION_TYPE * @p ctype was not a supported compression type. * @retval ::WIMLIB_ERR_INVALID_PARAM * @p max_block_size was invalid for the compression type, or @p * decompressor_ret was @c NULL. * @retval ::WIMLIB_ERR_NOMEM * Insufficient memory to allocate the decompressor. */ extern int wimlib_create_decompressor(enum wimlib_compression_type ctype, size_t max_block_size, struct wimlib_decompressor **decompressor_ret); /** * Decompress a buffer of data. * * @param compressed_data * Buffer containing the data to decompress. * @param compressed_size * Size, in bytes, of the data to decompress. * @param uncompressed_data * Buffer into which to write the uncompressed data. * @param uncompressed_size * Size, in bytes, of the data when uncompressed. This cannot exceed the * @p max_block_size with which wimlib_create_decompressor() was called. * (If it does, the data will not be decompressed and a nonzero value will * be returned.) * @param decompressor * A decompressor previously allocated with wimlib_create_decompressor(). * * @return 0 on success; nonzero on failure. * * No specific error codes are defined; any nonzero value indicates that the * decompression failed. This can only occur if the data is truly invalid; * there will never be transient errors like "out of memory", for example. * * This function requires that the exact uncompressed size of the data be passed * as the @p uncompressed_size parameter. If this is not done correctly, * decompression may fail or the data may be decompressed incorrectly. */ extern int wimlib_decompress(const void *compressed_data, size_t compressed_size, void *uncompressed_data, size_t uncompressed_size, struct wimlib_decompressor *decompressor); /** * Free a decompressor previously allocated with wimlib_create_decompressor(). * * @param decompressor * The decompressor to free. If @c NULL, no action is taken. */ extern void wimlib_free_decompressor(struct wimlib_decompressor *decompressor); /** * @} */ #ifdef __cplusplus } #endif #endif /* _WIMLIB_H */ wimlib-1.13.1/config.h.in0000644000175000017500000001313713464166632012044 00000000000000/* config.h.in. Generated from configure.ac by autoheader. */ /* Define if building universal (internal helper macro) */ #undef AC_APPLE_UNIVERSAL_BUILD /* Define to 1 if including assertions */ #undef ENABLE_ASSERTIONS /* Define to 1 if including error messages */ #undef ENABLE_ERROR_MESSAGES /* Define to 1 to support multithreaded compression */ #undef ENABLE_MULTITHREADED_COMPRESSION /* Define to 1 if using SSSE3 implementation of SHA-1 */ #undef ENABLE_SSSE3_SHA1 /* Define to 1 to enable supporting code for tests */ #undef ENABLE_TEST_SUPPORT /* Define to 1 if you have the header file. */ #undef HAVE_ALLOCA_H /* Define to 1 if you have the header file. */ #undef HAVE_BYTESWAP_H /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H /* Define to 1 if you have the header file. */ #undef HAVE_ENDIAN_H /* Define to 1 if you have the header file. */ #undef HAVE_ERRNO_H /* Define to 1 if you have the `fdopendir' function. */ #undef HAVE_FDOPENDIR /* Define to 1 if you have the `flock' function. */ #undef HAVE_FLOCK /* Define to 1 if you have the `fsetxattr' function. */ #undef HAVE_FSETXATTR /* Define to 1 if you have the `fstatat' function. */ #undef HAVE_FSTATAT /* Define to 1 if you have the `futimens' function. */ #undef HAVE_FUTIMENS /* Define to 1 if you have the `getopt_long_only' function. */ #undef HAVE_GETOPT_LONG_ONLY /* Define to 1 if you have the header file. */ #undef HAVE_GLOB_H /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the `lgetxattr' function. */ #undef HAVE_LGETXATTR /* Define to 1 if you have the `rt' library (-lrt). */ #undef HAVE_LIBRT /* Define to 1 if you have the `llistxattr' function. */ #undef HAVE_LLISTXATTR /* Define to 1 if you have the `lsetxattr' function. */ #undef HAVE_LSETXATTR /* Define to 1 if you have the header file. */ #undef HAVE_MACHINE_ENDIAN_H /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Define to 1 if you have the `mempcpy' function. */ #undef HAVE_MEMPCPY /* Define to 1 if the system headers define a system call number for getrandom() */ #undef HAVE_NR_GETRANDOM /* Define to 1 if you have the `openat' function. */ #undef HAVE_OPENAT /* Define to 1 if you have the `posix_fallocate' function. */ #undef HAVE_POSIX_FALLOCATE /* Define if you have POSIX threads libraries and header files. */ #undef HAVE_PTHREAD /* Have PTHREAD_PRIO_INHERIT. */ #undef HAVE_PTHREAD_PRIO_INHERIT /* Define to 1 if you have the `readlinkat' function. */ #undef HAVE_READLINKAT /* Define to 1 if stat() supports nanosecond precision timestamps */ #undef HAVE_STAT_NANOSECOND_PRECISION /* Define to 1 if you have the header file. */ #undef HAVE_STDARG_H /* Define to 1 if you have the header file. */ #undef HAVE_STDDEF_H /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_BYTEORDER_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_ENDIAN_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_FILE_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SYSCALL_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SYSCTL_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TIMES_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_XATTR_H /* Define to 1 if you have the header file. */ #undef HAVE_TIME_H /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the `utimensat' function. */ #undef HAVE_UTIMENSAT /* Define to 1 if you have the header file. */ #undef HAVE_UTIME_H /* Define to the sub-directory where libtool stores uninstalled libraries. */ #undef LT_OBJDIR /* Name of package */ #undef PACKAGE /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to necessary symbol if this constant uses a non-standard name on your system. */ #undef PTHREAD_CREATE_JOINABLE /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS /* Version number of package */ #undef VERSION /* Define to 1 if using FUSE support */ #undef WITH_FUSE /* Define to 1 if using libcrypto SHA-1 */ #undef WITH_LIBCRYPTO /* Define to 1 if using NTFS-3G support */ #undef WITH_NTFS_3G /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined AC_APPLE_UNIVERSAL_BUILD # if defined __BIG_ENDIAN__ # define WORDS_BIGENDIAN 1 # endif #else # ifndef WORDS_BIGENDIAN # undef WORDS_BIGENDIAN # endif #endif wimlib-1.13.1/aclocal.m40000644000175000017500000015666113464166626011676 00000000000000# generated automatically by aclocal 1.16.1 -*- Autoconf -*- # Copyright (C) 1996-2018 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, [m4_warning([this file was generated for autoconf 2.69. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) # pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # serial 11 (pkg-config-0.29.1) dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2 of the License, or dnl (at your option) any later version. dnl dnl This program is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dnl General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA dnl 02111-1307, USA. dnl dnl As a special exception to the GNU General Public License, if you dnl distribute this file as part of a program that contains a dnl configuration script generated by Autoconf, you may include it under dnl the same distribution terms that you use for the rest of that dnl program. dnl PKG_PREREQ(MIN-VERSION) dnl ----------------------- dnl Since: 0.29 dnl dnl Verify that the version of the pkg-config macros are at least dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's dnl installed version of pkg-config, this checks the developer's version dnl of pkg.m4 when generating configure. dnl dnl To ensure that this macro is defined, also add: dnl m4_ifndef([PKG_PREREQ], dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], [m4_define([PKG_MACROS_VERSION], [0.29.1]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) dnl ---------------------------------- dnl Since: 0.16 dnl dnl Search for the pkg-config tool and set the PKG_CONFIG variable to dnl first found in the path. Checks that the version of pkg-config found dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is dnl used since that's the first version where most current features of dnl pkg-config existed. AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])dnl PKG_PROG_PKG_CONFIG dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------------------------------- dnl Since: 0.18 dnl dnl Check to see whether a particular set of modules exists. Similar to dnl PKG_CHECK_MODULES(), but does not set variables or print errors. dnl dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) dnl only at the first occurence in configure.ac, so if the first place dnl it's called might be skipped (such as if it is within an "if", you dnl have to call PKG_CHECK_EXISTS manually AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) dnl --------------------------------------------- dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting dnl pkg_failed based on the result. m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])dnl _PKG_CONFIG dnl _PKG_SHORT_ERRORS_SUPPORTED dnl --------------------------- dnl Internal check to see if pkg-config supports short errors. AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])dnl _PKG_SHORT_ERRORS_SUPPORTED dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl -------------------------------------------------------------- dnl Since: 0.4.0 dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES might not happen, you should be sure to include an dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $1]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])dnl PKG_CHECK_MODULES dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl --------------------------------------------------------------------- dnl Since: 0.29 dnl dnl Checks for existence of MODULES and gathers its build flags with dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags dnl and VARIABLE-PREFIX_LIBS from --libs. dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to dnl include an explicit call to PKG_PROG_PKG_CONFIG in your dnl configure.ac. AC_DEFUN([PKG_CHECK_MODULES_STATIC], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl _save_PKG_CONFIG=$PKG_CONFIG PKG_CONFIG="$PKG_CONFIG --static" PKG_CHECK_MODULES($@) PKG_CONFIG=$_save_PKG_CONFIG[]dnl ])dnl PKG_CHECK_MODULES_STATIC dnl PKG_INSTALLDIR([DIRECTORY]) dnl ------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable pkgconfigdir as the location where a module dnl should install pkg-config .pc files. By default the directory is dnl $libdir/pkgconfig, but the default can be changed by passing dnl DIRECTORY. The user can override through the --with-pkgconfigdir dnl parameter. AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, [with_pkgconfigdir=]pkg_default) AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_INSTALLDIR dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) dnl -------------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable noarch_pkgconfigdir as the location where a dnl module should install arch-independent pkg-config .pc files. By dnl default the directory is $datadir/pkgconfig, but the default can be dnl changed by passing DIRECTORY. The user can override through the dnl --with-noarch-pkgconfigdir parameter. AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([noarch-pkgconfigdir], [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, [with_noarch_pkgconfigdir=]pkg_default) AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_NOARCH_INSTALLDIR dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------- dnl Since: 0.28 dnl dnl Retrieves the value of the pkg-config variable for the given module. AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES, dnl [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND], dnl [DESCRIPTION], [DEFAULT]) dnl ------------------------------------------ dnl dnl Prepare a "--with-" configure option using the lowercase dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and dnl PKG_CHECK_MODULES in a single macro. AC_DEFUN([PKG_WITH_MODULES], [ m4_pushdef([with_arg], m4_tolower([$1])) m4_pushdef([description], [m4_default([$5], [build with ]with_arg[ support])]) m4_pushdef([def_arg], [m4_default([$6], [auto])]) m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes]) m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no]) m4_case(def_arg, [yes],[m4_pushdef([with_without], [--without-]with_arg)], [m4_pushdef([with_without],[--with-]with_arg)]) AC_ARG_WITH(with_arg, AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),, [AS_TR_SH([with_]with_arg)=def_arg]) AS_CASE([$AS_TR_SH([with_]with_arg)], [yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)], [auto],[PKG_CHECK_MODULES([$1],[$2], [m4_n([def_action_if_found]) $3], [m4_n([def_action_if_not_found]) $4])]) m4_popdef([with_arg]) m4_popdef([description]) m4_popdef([def_arg]) ])dnl PKG_WITH_MODULES dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES, dnl [DESCRIPTION], [DEFAULT]) dnl ----------------------------------------------- dnl dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES dnl check._[VARIABLE-PREFIX] is exported as make variable. AC_DEFUN([PKG_HAVE_WITH_MODULES], [ PKG_WITH_MODULES([$1],[$2],,,[$3],[$4]) AM_CONDITIONAL([HAVE_][$1], [test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"]) ])dnl PKG_HAVE_WITH_MODULES dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES, dnl [DESCRIPTION], [DEFAULT]) dnl ------------------------------------------------------ dnl dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make dnl and preprocessor variable. AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES], [ PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4]) AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"], [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])]) ])dnl PKG_HAVE_DEFINE_WITH_MODULES # Copyright (C) 2002-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.16' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. m4_if([$1], [1.16.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) # _AM_AUTOCONF_VERSION(VERSION) # ----------------------------- # aclocal traces this macro to find the Autoconf version. # This is a private macro too. Using m4_define simplifies # the logic in aclocal, which can simply ignore this definition. m4_define([_AM_AUTOCONF_VERSION], []) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.16.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # Copyright (C) 2011-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_AR([ACT-IF-FAIL]) # ------------------------- # Try to determine the archiver interface, and trigger the ar-lib wrapper # if it is needed. If the detection of archiver interface fails, run # ACT-IF-FAIL (default is to abort configure with a proper error message). AC_DEFUN([AM_PROG_AR], [AC_BEFORE([$0], [LT_INIT])dnl AC_BEFORE([$0], [AC_PROG_LIBTOOL])dnl AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([ar-lib])dnl AC_CHECK_TOOLS([AR], [ar lib "link -lib"], [false]) : ${AR=ar} AC_CACHE_CHECK([the archiver ($AR) interface], [am_cv_ar_interface], [AC_LANG_PUSH([C]) am_cv_ar_interface=ar AC_COMPILE_IFELSE([AC_LANG_SOURCE([[int some_variable = 0;]])], [am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&AS_MESSAGE_LOG_FD' AC_TRY_EVAL([am_ar_try]) if test "$ac_status" -eq 0; then am_cv_ar_interface=ar else am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&AS_MESSAGE_LOG_FD' AC_TRY_EVAL([am_ar_try]) if test "$ac_status" -eq 0; then am_cv_ar_interface=lib else am_cv_ar_interface=unknown fi fi rm -f conftest.lib libconftest.a ]) AC_LANG_POP([C])]) case $am_cv_ar_interface in ar) ;; lib) # Microsoft lib, so override with the ar-lib wrapper script. # FIXME: It is wrong to rewrite AR. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__AR in this case, # and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something # similar. AR="$am_aux_dir/ar-lib $AR" ;; unknown) m4_default([$1], [AC_MSG_ERROR([could not determine $AR interface])]) ;; esac AC_SUBST([AR])dnl ]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- # Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to # '$srcdir', '$srcdir/..', or '$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is '.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], [AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ([2.52])dnl m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl m4_define([_AM_COND_VALUE_$1], [$2])dnl if $2; then $1_TRUE= $1_FALSE='#' else $1_TRUE='#' $1_FALSE= fi AC_CONFIG_COMMANDS_PRE( [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then AC_MSG_ERROR([[conditional "$1" was never defined. Usually this means the macro was only invoked conditionally.]]) fi])]) # Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # _AM_DEPENDENCIES(NAME) # ---------------------- # See how the compiler implements dependency checking. # NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular # dependency, and given that the user is not expected to run this macro, # just rely on AC_PROG_CC. AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], [$1], [CXX], [depcc="$CXX" am_compiler_list=], [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], [$1], [UPC], [depcc="$UPC" am_compiler_list=], [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], [depcc="$$1" am_compiler_list=]) AC_CACHE_CHECK([dependency style of $depcc], [am_cv_$1_dependencies_compiler_type], [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_$1_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` fi am__universal=false m4_case([$1], [CC], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac], [CXX], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac]) for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_$1_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_$1_dependencies_compiler_type=none fi ]) AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) AM_CONDITIONAL([am__fastdep$1], [ test "x$enable_dependency_tracking" != xno \ && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) ]) # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. # This macro is AC_REQUIREd in _AM_DEPENDENCIES. AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl ]) # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE([dependency-tracking], [dnl AS_HELP_STRING( [--enable-dependency-tracking], [do not reject slow dependency extractors]) AS_HELP_STRING( [--disable-dependency-tracking], [speeds up one-time build])]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) AC_SUBST([AMDEPBACKSLASH])dnl _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl AC_SUBST([am__nodep])dnl _AM_SUBST_NOTMAKE([am__nodep])dnl ]) # Generate code to set up dependency tracking. -*- Autoconf -*- # Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. # TODO: see whether this extra hack can be removed once we start # requiring Autoconf 2.70 or later. AS_CASE([$CONFIG_FILES], [*\'*], [eval set x "$CONFIG_FILES"], [*], [set x $CONFIG_FILES]) shift # Used to flag and report bootstrapping failures. am_rc=0 for am_mf do # Strip MF so we end up with the name of the file. am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile which includes # dependency-tracking related rules and includes. # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ || continue am_dirpart=`AS_DIRNAME(["$am_mf"])` am_filepart=`AS_BASENAME(["$am_mf"])` AM_RUN_LOG([cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles]) || am_rc=$? done if test $am_rc -ne 0; then AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments for automatic dependency tracking. Try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking).]) fi AS_UNSET([am_dirpart]) AS_UNSET([am_filepart]) AS_UNSET([am_mf]) AS_UNSET([am_rc]) rm -f conftest-deps.mk } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS # AM_OUTPUT_DEPENDENCY_COMMANDS # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # # This code is only required when automatic dependency tracking is enabled. # This creates each '.Po' and '.Plo' makefile fragment that we'll need in # order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])]) # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC]) [_AM_PROG_CC_C_O ]) # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- # The call with PACKAGE and VERSION arguments is the old style # call (pre autoconf-2.50), which is being phased out. PACKAGE # and VERSION should now be passed to AC_INIT and removed from # the call to AM_INIT_AUTOMAKE. # We support both call styles for the transition. After # the next Automake release, Autoconf can make the AC_INIT # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.65])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl # test to see if srcdir already configured if test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], [AC_DIAGNOSE([obsolete], [$0: two- and three-arguments forms are deprecated.]) m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if( m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), [ok:ok],, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, [AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) AM_MISSING_PROG([AUTOCONF], [autoconf]) AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) AM_MISSING_PROG([AUTOHEADER], [autoheader]) AM_MISSING_PROG([MAKEINFO], [makeinfo]) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES([CC])], [m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES([CXX])], [m4_define([AC_PROG_CXX], m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES([OBJC])], [m4_define([AC_PROG_OBJC], m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], [_AM_DEPENDENCIES([OBJCXX])], [m4_define([AC_PROG_OBJCXX], m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl ]) AC_REQUIRE([AM_SILENT_RULES])dnl dnl The testsuite driver may need to know about EXEEXT, so add the dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) fi fi dnl The trailing newline in this macro's definition is deliberate, for dnl backward compatibility and to allow trailing 'dnl'-style comments dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. ]) dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the # loop where config.status creates the headers, so we can generate # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. _am_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) # Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi AC_SUBST([install_sh])]) # Copyright (C) 2003-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MAKE_INCLUDE() # ----------------- # Check whether make has an 'include' directive that can support all # the idioms we need for our automatic dependency tracking code. AC_DEFUN([AM_MAKE_INCLUDE], [AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive]) cat > confinc.mk << 'END' am__doit: @echo this is the am__doit target >confinc.out .PHONY: am__doit END am__include="#" am__quote= # BSD make does it like this. echo '.include "confinc.mk" # ignored' > confmf.BSD # Other make implementations (GNU, Solaris 10, AIX) do it like this. echo 'include confinc.mk # ignored' > confmf.GNU _am_result=no for s in GNU BSD; do AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out]) AS_CASE([$?:`cat confinc.out 2>/dev/null`], ['0:this is the am__doit target'], [AS_CASE([$s], [BSD], [am__include='.include' am__quote='"'], [am__include='include' am__quote=''])]) if test "$am__include" != "#"; then _am_result="yes ($s style)" break fi done rm -f confinc.* confmf.* AC_MSG_RESULT([${_am_result}]) AC_SUBST([am__include])]) AC_SUBST([am__quote])]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- # Copyright (C) 1997-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it is modern enough. # If it is, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= AC_MSG_WARN(['missing' script is too old or missing]) fi ]) # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) # _AM_SET_OPTION(NAME) # -------------------- # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), [1])]) # _AM_SET_OPTIONS(OPTIONS) # ------------------------ # OPTIONS is a space-separated list of Automake options. AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) # ------------------------------------------- # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) # Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_CC_C_O # --------------- # Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC # to automatically call this. AC_DEFUN([_AM_PROG_CC_C_O], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([compile])dnl AC_LANG_PUSH([C])dnl AC_CACHE_CHECK( [whether $CC understands -c and -o together], [am_cv_prog_cc_c_o], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i]) if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) # Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_RUN_LOG(COMMAND) # ------------------- # Run COMMAND, save the exit status in ac_status, and log it. # (This has been adapted from Autoconf's _AC_RUN_LOG macro.) AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD (exit $ac_status); }]) # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[[\\\"\#\$\&\'\`$am_lf]]*) AC_MSG_ERROR([unsafe absolute working directory name]);; esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$[*]" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$[*]" != "X $srcdir/configure conftest.file" \ && test "$[*]" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken alias in your environment]) fi if test "$[2]" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$[2]" = conftest.file ) then # Ok. : else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi AC_MSG_RESULT([yes]) # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi AC_CONFIG_COMMANDS_PRE( [AC_MSG_CHECKING([that generated files are newer than configure]) if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi AC_MSG_RESULT([done])]) rm -f conftest.file ]) # Copyright (C) 2009-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SILENT_RULES([DEFAULT]) # -------------------------- # Enable less verbose build rules; with the default set to DEFAULT # ("yes" being less verbose, "no" or empty being verbose). AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [dnl AS_HELP_STRING( [--enable-silent-rules], [less verbose build output (undo: "make V=1")]) AS_HELP_STRING( [--disable-silent-rules], [verbose build output (undo: "make V=0")])dnl ]) case $enable_silent_rules in @%:@ ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; esac dnl dnl A few 'make' implementations (e.g., NonStop OS and NextStep) dnl do not support nested variable expansions. dnl See automake bug#9928 and bug#10237. am_make=${MAKE-make} AC_CACHE_CHECK([whether $am_make supports nested variables], [am_cv_make_support_nested_variables], [if AS_ECHO([['TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi]) if test $am_cv_make_support_nested_variables = yes; then dnl Using '$V' instead of '$(V)' breaks IRIX make. AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AC_SUBST([AM_V])dnl AM_SUBST_NOTMAKE([AM_V])dnl AC_SUBST([AM_DEFAULT_V])dnl AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl AC_SUBST([AM_DEFAULT_VERBOSITY])dnl AM_BACKSLASH='\' AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) # Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_STRIP # --------------------- # One issue with vendor 'install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in "make install-strip", and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # Copyright (C) 2006-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) # AM_SUBST_NOTMAKE(VARIABLE) # -------------------------- # Public sister of _AM_SUBST_NOTMAKE. AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of 'v7', 'ustar', or 'pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar # AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' m4_if([$1], [v7], [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], [m4_case([$1], [ustar], [# The POSIX 1988 'ustar' format is defined with fixed-size fields. # There is notably a 21 bits limit for the UID and the GID. In fact, # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 # and bug#13588). am_max_uid=2097151 # 2^21 - 1 am_max_gid=$am_max_uid # The $UID and $GID variables are not portable, so we need to resort # to the POSIX-mandated id(1) utility. Errors in the 'id' calls # below are definitely unexpected, so allow the users to see them # (that is, avoid stderr redirection). am_uid=`id -u || echo unknown` am_gid=`id -g || echo unknown` AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) if test $am_uid -le $am_max_uid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) if test $am_gid -le $am_max_gid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi], [pax], [], [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_$1-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x $1 -w "$$tardir"' am__tar_='pax -L -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1 -L' am__tar_='find "$tardir" -print | cpio -o -H $1 -L' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar /dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR m4_include([m4/ax_pthread.m4]) m4_include([m4/libtool.m4]) m4_include([m4/ltoptions.m4]) m4_include([m4/ltsugar.m4]) m4_include([m4/ltversion.m4]) m4_include([m4/lt~obsolete.m4]) m4_include([m4/nasm.m4]) wimlib-1.13.1/debian/0000755000175000017500000000000013464166215011313 500000000000000wimlib-1.13.1/debian/copyright0000644000175000017500000000211213160354224013152 00000000000000Format: http://dep.debian.net/deps/dep5 Upstream-Name: wimlib Source: https://wimlib.net Files: * Copyright: 2012-2016 Eric Biggers License: GPLv3+ with exception Unless otherwise specified, wimlib and its associated programs, scripts, documentation, and other files may be redistributed and/or modified under the terms of the GNU General Public License; either version 3 of the License, or (at your option) any later version. There is NO WARRANTY, to the extent permitted by law. See the file COPYING.GPLv3 for more details. . Alternatively, when not prohibited by conflict with a third-party software license, the library portion of wimlib may be redistributed and/or modified under the terms of the GNU Lesser General Public License; either version 3 of the License, or (at your option) any later version. There is NO WARRANTY, to the extent permitted by law. See the file COPYING.LGPLv3 for more details. Files: debian/* License: Public domain The Debian packaging scripts are free to be redistributed and/or modified with no restrictions. wimlib-1.13.1/debian/wimtools.docs0000644000175000017500000000001413016523676013756 00000000000000NEWS README wimlib-1.13.1/debian/compat0000644000175000017500000000000212260745400012421 000000000000007 wimlib-1.13.1/debian/control0000644000175000017500000000541113160354224012627 00000000000000Source: wimlib Priority: optional Maintainer: Eric Biggers Build-Depends: debhelper (>= 8.9.7), autotools-dev, pkg-config, libfuse-dev, libxml2-dev, libssl-dev, ntfs-3g-dev (>= 2011.4.12), attr Build-Depends-Indep: doxygen Standards-Version: 3.9.3 Section: libs Homepage: https://wimlib.net Vcs-Git: git://wimlib.net/wimlib Package: wimlib15 Section: libs Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Suggests: wimtools Description: Library to extract, create, modify, and mount WIM files wimlib is a C library for extracting, creating, modifying, and mounting WIM (Windows Imaging) files. WIM is an archive format designed primarily for archiving Windows filesystems. It features single-instancing and LZ77-based compression, and is used by Microsoft to distribute and deploy Windows Vista and later. wimlib is an independent implementation of an API for handling WIM files, available on both UNIX-like systems and Windows, that provides features similar to Microsoft's WIMGAPI, as well as additional features such as support for pipable WIM files and programatically making changes to WIM images without mounting them. Package: wimtools Section: utils Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Suggests: genisoimage, mtools, syslinux, cabextract Description: Tools to extract, create, modify, and mount WIM files Tools to extract, create, modify, and mount WIM (Windows Imaging) files. WIM is an archive format designed primarily for archiving Windows filesystems. It features single-instancing and LZ77-based compression and is used by Microsoft to distribute and deploy Windows Vista and later. WIM files are normally created by using the `imagex.exe' utility on Windows, but this package contains a free implementation of ImageX called "wimlib-imagex" that is designed to work on both UNIX-like systems and Windows. . In addition to the usual extract/create/update support, wimlib-imagex allows you to mount WIM images readonly or read-write, and it even allows you to extract or create a WIM image directly to/from an unmounted NTFS volume. This makes it possible to, from Linux, back up or deploy a Windows OS directly to or from a WIM file, such as the install.wim distributed on the Windows installation media. . This package also contains a script to make a customized Windows PE image based on the capabilities provided by wimlib-imagex. Package: wimlib-dev Section: libdevel Architecture: any Depends: wimlib15 (= ${binary:Version}), ${misc:Depends} Suggests: wimlib-doc Description: wimlib - development files Development files for wimlib Package: wimlib-doc Section: doc Architecture: all Depends: ${misc:Depends} Description: wimlib - API documentation API documentation for wimlib wimlib-1.13.1/debian/wimlib-doc.examples0000644000175000017500000000001312260745400015003 00000000000000examples/* wimlib-1.13.1/debian/wimtools.install0000644000175000017500000000003712260745400014470 00000000000000usr/bin/* usr/share/man/man1/* wimlib-1.13.1/debian/wimlib-dev.install0000644000175000017500000000012412260745400014647 00000000000000usr/include/wimlib.h usr/lib/libwim.a usr/lib/libwim.so usr/lib/pkgconfig/wimlib.pc wimlib-1.13.1/debian/rules0000744000175000017500000000105512260745400012302 00000000000000#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. export DH_VERBOSE=1 %: dh $@ override_dh_auto_build-indep: set -e; if type doxygen >/dev/null 2>/dev/null; \ then \ cd doc && doxygen; \ fi wimlib-1.13.1/debian/wimlib-doc.docs0000644000175000017500000000002513016523676014131 00000000000000doc/html NEWS README wimlib-1.13.1/debian/source/0000755000175000017500000000000012260745400012603 500000000000000wimlib-1.13.1/debian/source/format0000644000175000017500000000001512260745400013732 000000000000003.0 (native) wimlib-1.13.1/debian/wimlib15.install0000644000175000017500000000002413016523676014251 00000000000000usr/lib/libwim.so.* wimlib-1.13.1/debian/changelog0000644000175000017500000001276713464166215013122 00000000000000wimlib (1.13.1-1) unstable; urgency=low * Update to v1.13.1 -- Eric Biggers Mon, 06 May 2019 19:23:40 -0700 wimlib (1.13.0-1) unstable; urgency=low * Update to v1.13.0 -- Eric Biggers Thu, 22 Nov 2018 17:04:35 -0800 wimlib (1.12.0-1) unstable; urgency=low * Update to v1.12.0 -- Eric Biggers Sat, 29 Jul 2017 12:01:11 -0700 wimlib (1.11.0-1) unstable; urgency=low * Update to v1.11.0 -- Eric Biggers Tue, 17 Jan 2017 19:41:32 -0800 wimlib (1.10.0-1) unstable; urgency=low * Update to v1.10.0 -- Eric Biggers Fri, 19 Aug 2016 20:12:30 -0700 wimlib (1.9.2-1) unstable; urgency=low * Update to v1.9.2 -- Eric Biggers Tue, 31 May 2016 20:40:27 -0500 wimlib (1.9.1-1) unstable; urgency=low * Update to v1.9.1 -- Eric Biggers Fri, 11 Mar 2016 21:54:00 -0600 wimlib (1.9.0-1) unstable; urgency=low * Update to v1.9.0 -- Eric Biggers Sat, 30 Jan 2016 23:58:54 -0600 wimlib (1.8.3-1) unstable; urgency=low * Update to v1.8.3 -- Eric Biggers Sat, 14 Nov 2015 13:29:29 -0600 wimlib (1.8.2-1) unstable; urgency=low * Update to v1.8.2 -- Eric Biggers Sat, 22 Aug 2015 09:19:10 -0500 wimlib (1.8.1-1) unstable; urgency=low * Update to v1.8.1 -- Eric Biggers Sat, 16 May 2015 12:35:15 -0500 wimlib (1.8.0-1) unstable; urgency=low * Update to v1.8.0 -- Eric Biggers Tue, 24 Feb 2015 22:54:49 -0600 wimlib (1.7.4-1) unstable; urgency=low * Update to v1.7.4 -- Eric Biggers Fri, 02 Jan 2015 20:01:28 -0600 wimlib (1.7.3-1) unstable; urgency=low * Update to v1.7.3 -- Eric Biggers Thu, 13 Nov 2014 18:11:27 -0600 wimlib (1.7.2-1) unstable; urgency=low * Update to v1.7.2 -- Eric Biggers Thu, 02 Oct 2014 20:24:27 -0500 wimlib (1.7.1-1) unstable; urgency=low * Update to v1.7.1 -- Eric Biggers Wed, 06 Aug 2014 22:34:49 -0500 wimlib (1.7.0-1) unstable; urgency=low * Update to v1.7.0 -- Eric Biggers Sat, 07 Jun 2014 21:34:56 -0500 wimlib (1.6.2-1) unstable; urgency=low * Update to v1.6.2 -- Eric Biggers Fri, 14 Mar 2014 15:59:59 -0500 wimlib (1.6.1-1) unstable; urgency=low * Update to v1.6.1 -- Eric Biggers Sun, 12 Jan 2014 23:13:44 -0600 wimlib (1.6.0-1) unstable; urgency=low * Update to v1.6.0 -- Eric Biggers Sat, 28 Dec 2013 11:13:54 -0600 wimlib (1.5.3-1) unstable; urgency=low * Update to v1.5.3 -- Eric Biggers Sun, 08 Dec 2013 00:34:21 -0600 wimlib (1.5.2-1) unstable; urgency=low * Update to v1.5.2 -- Eric Biggers Sun, 17 Nov 2013 17:10:43 -0600 wimlib (1.5.1-1) unstable; urgency=low * Update to v1.5.1 -- Eric Biggers Fri, 11 Oct 2013 09:34:20 -0500 wimlib (1.5.0-1) unstable; urgency=low * Update to v1.5.0 -- Eric Biggers Fri, 16 Aug 2013 20:51:08 -0500 wimlib (1.4.2-1) unstable; urgency=low * Update to v1.4.2 -- Eric Biggers Tue, 11 Jun 2013 19:46:09 -0500 wimlib (1.4.1-1) precise; urgency=low * Update to v1.4.1 -- Eric Biggers Mon, 20 May 2013 14:01:44 -0500 wimlib (1.4.0-1) unstable; urgency=low * Update to v1.4.0; moved wimlib-imagex and mkwinpeimg to "wimtools" package. -- Eric Biggers Sun, 12 May 2013 22:59:55 -0500 wimlib (1.3.3-1) unstable; urgency=low * Update to v1.3.3 -- Eric Biggers Mon, 08 Apr 2013 00:16:50 -0500 wimlib (1.3.2-1) unstable; urgency=low * Update to v1.3.2 -- Eric Biggers Sat, 23 Mar 2013 20:00:14 -0500 wimlib (1.3.1-1) unstable; urgency=low * Update to v1.3.1 -- Eric Biggers Fri, 22 Mar 2013 01:05:27 -0500 wimlib (1.3.0-1) unstable; urgency=low * Update to v1.3.0 -- Eric Biggers Sun, 10 Mar 2013 18:46:29 -0700 wimlib (1.2.6-1) unstable; urgency=low * Update to v1.2.6 -- Eric Biggers Tue, 05 Mar 2013 14:26:59 -0600 wimlib (1.2.5-1) unstable; urgency=low * Update to v1.2.5 -- Eric Biggers Tue, 05 Feb 2013 20:26:47 -0600 wimlib (1.2.4-1) unstable; urgency=low * Update to v1.2.4 -- Eric Biggers Tue, 29 Jan 2013 20:19:29 -0600 wimlib (1.2.3-1) unstable; urgency=low * Update to v1.2.3 -- Eric Biggers Mon, 31 Dec 2012 14:33:36 -0600 wimlib (1.2.2-1) unstable; urgency=low * Update to v1.2.2 -- Eric Biggers Fri, 21 Dec 2012 12:39:22 -0600 wimlib (1.2.1-1) unstable; urgency=low * Update to v1.2.1 -- Eric Biggers Tue, 18 Dec 2012 11:25:02 -0600 wimlib (1.2.0-1) unstable; urgency=low * Update to v1.2.0 -- Eric Biggers Thu, 22 Nov 2012 14:35:33 -0600 wimlib (1.1.0-1) unstable; urgency=low * Update to v1.1.0 -- Eric Biggers Sun, 18 Nov 2012 13:49:54 -0600 wimlib (1.0.4-1) unstable; urgency=low * Update to v1.0.4 -- Eric Biggers Mon, 29 Oct 2012 03:57:13 +0000 wimlib (0.6.3-1) unstable; urgency=low * Initial release of Debian package -- Eric Biggers Tue, 01 May 2012 23:48:40 -0500 wimlib-1.13.1/debian/watch0000644000175000017500000000020413160354224012250 00000000000000# See uscan(1) for format # Compulsory line, this is a version 3 file version=3 https://wimlib.net/downloads/wimlib-(.*)\.tar\.gz wimlib-1.13.1/Makefile.am0000644000175000017500000002215613464166215012053 00000000000000############################################################################## # General # ############################################################################## ACLOCAL_AMFLAGS = -I m4 AM_CPPFLAGS = -I$(top_srcdir)/include $(PLATFORM_CPPFLAGS) \ -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE # Note: -std=gnu99 instead of -std=c99 is needed for unnamed structs and unions, # which are in C11 but not C99. But we can't yet actually use -std=c11 because # we want to support older versions of gcc. AM_CFLAGS = -std=gnu99 $(PLATFORM_CFLAGS) -fno-common \ -Wmissing-prototypes -Wstrict-prototypes \ -Wundef -Wno-pointer-sign AM_LDFLAGS = $(PLATFORM_LDFLAGS) EXTRA_DIST = README README.WINDOWS \ COPYING COPYING.GPLv3 COPYING.LGPLv3 COPYING.CC0 \ examples \ debian rpm ############################################################################## # Library # ############################################################################## lib_LTLIBRARIES = libwim.la include_HEADERS = include/wimlib.h pkgconfigdir = @pkgconfigdir@ pkgconfig_DATA = wimlib.pc wimlib.pc: config.status libwim_la_SOURCES = \ src/add_image.c \ src/avl_tree.c \ src/blob_table.c \ src/compress.c \ src/compress_common.c \ src/compress_parallel.c \ src/compress_serial.c \ src/decompress.c \ src/decompress_common.c \ src/delete_image.c \ src/dentry.c \ src/divsufsort.c \ src/encoding.c \ src/error.c \ src/export_image.c \ src/extract.c \ src/file_io.c \ src/header.c \ src/inode.c \ src/inode_fixup.c \ src/inode_table.c \ src/integrity.c \ src/iterate_dir.c \ src/join.c \ src/lcpit_matchfinder.c \ src/lzms_common.c \ src/lzms_compress.c \ src/lzms_decompress.c \ src/lzx_common.c \ src/lzx_compress.c \ src/lzx_decompress.c \ src/metadata_resource.c \ src/mount_image.c \ src/pathlist.c \ src/paths.c \ src/pattern.c \ src/progress.c \ src/reference.c \ src/registry.c \ src/reparse.c \ src/resource.c \ src/scan.c \ src/security.c \ src/sha1.c \ src/solid.c \ src/split.c \ src/tagged_items.c \ src/template.c \ src/textfile.c \ src/timestamp.c \ src/update_image.c \ src/util.c \ src/verify.c \ src/wim.c \ src/write.c \ src/x86_cpu_features.c \ src/xml.c \ src/xml_windows.c \ src/xpress_compress.c \ src/xpress_decompress.c \ include/wimlib/alloca.h \ include/wimlib/apply.h \ include/wimlib/assert.h \ include/wimlib/avl_tree.h \ include/wimlib/bitops.h \ include/wimlib/blob_table.h \ include/wimlib/bt_matchfinder.h \ include/wimlib/case.h \ include/wimlib/compiler.h \ include/wimlib/compressor_ops.h \ include/wimlib/compress_common.h \ include/wimlib/chunk_compressor.h \ include/wimlib/decompressor_ops.h \ include/wimlib/decompress_common.h \ include/wimlib/dentry.h \ include/wimlib/divsufsort.h \ include/wimlib/encoding.h \ include/wimlib/endianness.h \ include/wimlib/error.h \ include/wimlib/file_io.h \ include/wimlib/glob.h \ include/wimlib/guid.h \ include/wimlib/hc_matchfinder.h \ include/wimlib/header.h \ include/wimlib/inode.h \ include/wimlib/inode_table.h \ include/wimlib/integrity.h \ include/wimlib/lcpit_matchfinder.h \ include/wimlib/list.h \ include/wimlib/lz_extend.h \ include/wimlib/lz_hash.h \ include/wimlib/lzms_common.h \ include/wimlib/lzms_constants.h \ include/wimlib/lzx_common.h \ include/wimlib/lzx_constants.h \ include/wimlib/metadata.h \ include/wimlib/object_id.h \ include/wimlib/pathlist.h \ include/wimlib/paths.h \ include/wimlib/pattern.h \ include/wimlib/progress.h \ include/wimlib/registry.h \ include/wimlib/reparse.h \ include/wimlib/resource.h \ include/wimlib/scan.h \ include/wimlib/security.h \ include/wimlib/security_descriptor.h \ include/wimlib/sha1.h \ include/wimlib/solid.h \ include/wimlib/tagged_items.h \ include/wimlib/textfile.h \ include/wimlib/timestamp.h \ include/wimlib/types.h \ include/wimlib/unaligned.h \ include/wimlib/unix_data.h \ include/wimlib/util.h \ include/wimlib/wim.h \ include/wimlib/write.h \ include/wimlib/x86_cpu_features.h \ include/wimlib/xattr.h \ include/wimlib/xml.h \ include/wimlib/xml_windows.h \ include/wimlib/xpress_constants.h if WITH_NTFS_3G libwim_la_SOURCES += src/ntfs-3g_apply.c \ src/ntfs-3g_capture.c \ include/wimlib/ntfs_3g.h endif if WINDOWS_NATIVE_BUILD libwim_la_SOURCES += src/wimboot.c \ src/win32_common.c \ src/win32_apply.c \ src/win32_capture.c \ src/win32_replacements.c \ src/win32_vss.c \ include/wimlib/wimboot.h \ include/wimlib/win32.h \ include/wimlib/win32_common.h \ include/wimlib/win32_vss.h \ include/wimlib/wof.h PLATFORM_LIBS = -lmsvcrt -lntdll else libwim_la_SOURCES += src/unix_apply.c \ src/unix_capture.c PLATFORM_LIBS = endif if ENABLE_TEST_SUPPORT libwim_la_SOURCES += src/test_support.c \ include/wimlib/test_support.h if WINDOWS_NATIVE_BUILD # Workaround for "multiple definition" error when math symbols are present in # both libmsvcrt.a and ntdll.a AM_LDFLAGS += -Wl,--allow-multiple-definition endif endif libwim_la_CFLAGS = \ $(AM_CFLAGS) \ $(PTHREAD_CFLAGS) \ $(LIBXML2_CFLAGS) \ $(LIBNTFS_3G_CFLAGS) \ $(LIBFUSE_CFLAGS) \ $(LIBCRYPTO_CFLAGS) libwim_la_LDFLAGS = $(AM_LDFLAGS) -version-info 31:0:16 libwim_la_LIBADD = \ $(PTHREAD_LIBS) \ $(LIBXML2_LIBS) \ $(LIBNTFS_3G_LIBS) \ $(LIBFUSE_LIBS) \ $(LIBRT_LIBS) \ $(LIBCRYPTO_LIBS) \ $(PLATFORM_LIBS) if ENABLE_SSSE3_SHA1 libwim_la_SOURCES += src/sha1-ssse3.asm libwim_la_LIBADD += src/sha1-ssse3.lo src/sha1-ssse3.lo:src/sha1-ssse3.asm $(LIBTOOL) --mode=compile $(srcdir)/build-aux/nasm_lt.sh \ $(NASM) $(NAFLAGS) $(NASM_PLATFORM_FLAGS) \ -DINTEL_SHA1_UPDATE_FUNCNAME=$(NASM_SYMBOL_PREFIX)sha1_transform_blocks_ssse3 \ -DINTEL_SHA1_UPDATE_DEFAULT_DISPATCH=$(NASM_SYMBOL_PREFIX)sha1_transform_blocks_default \ $< -o $@ endif EXTRA_DIST += build-aux/nasm_lt.sh ############################################################################## # Programs # ############################################################################## bin_PROGRAMS = wimlib-imagex dist_bin_SCRIPTS = programs/mkwinpeimg wimlib_imagex_SOURCES = \ programs/imagex.c \ include/wimlib.h \ include/wimlib_tchar.h if WINDOWS_NATIVE_BUILD wimlib_imagex_SOURCES += \ programs/imagex-win32.c \ programs/imagex-win32.h \ programs/wgetopt.c \ programs/wgetopt.h endif wimlib_imagex_CFLAGS = $(AM_CFLAGS) -Wno-deprecated-declarations wimlib_imagex_LDADD = $(top_builddir)/libwim.la wimlib_imagex_cmds = \ append \ apply \ capture \ delete \ dir \ export \ extract \ info \ join \ mount \ mountrw \ optimize \ split \ unmount \ update \ verify ############################################################################## # Hooks # ############################################################################## install-exec-hook: for cmd in $(wimlib_imagex_cmds); do \ cd $(DESTDIR)$(bindir) && \ ln -f wimlib-imagex wim$${cmd}; \ done install-data-hook: for cmd in $(wimlib_imagex_cmds); do \ cd $(DESTDIR)$(mandir)/man1 && \ ln -sf wim$${cmd}.1 wimlib-imagex-$${cmd}.1; \ done uninstall-hook: for cmd in $(wimlib_imagex_cmds); do \ rm -f $(DESTDIR)$(bindir)/wim$${cmd}; \ rm -f $(DESTDIR)$(mandir)/man1/wim$${cmd}.1; \ done ############################################################################## # Documentation # ############################################################################## man1_MANS = \ doc/man1/wimlib-imagex.1 \ doc/man1/wimappend.1 \ doc/man1/wimapply.1 \ doc/man1/wimcapture.1 \ doc/man1/wimdelete.1 \ doc/man1/wimdir.1 \ doc/man1/wimexport.1 \ doc/man1/wimextract.1 \ doc/man1/wiminfo.1 \ doc/man1/wimjoin.1 \ doc/man1/wimmount.1 \ doc/man1/wimmountrw.1 \ doc/man1/wimoptimize.1 \ doc/man1/wimsplit.1 \ doc/man1/wimunmount.1 \ doc/man1/wimupdate.1 \ doc/man1/wimverify.1 \ doc/man1/mkwinpeimg.1 EXTRA_DIST += $(man1_MANS) ############################################################################## # Tests # ############################################################################## check_PROGRAMS = tests/tree-cmp tests_tree_cmp_SOURCES = tests/tree-cmp.c dist_check_SCRIPTS = tests/test-imagex \ tests/test-imagex-capture_and_apply \ tests/test-imagex-update_and_extract if WITH_FUSE dist_check_SCRIPTS += tests/test-imagex-mount endif if WITH_NTFS_3G dist_check_SCRIPTS += tests/test-imagex-ntfs endif EXTRA_DIST += \ tests/common_tests.sh \ tests/exclusionlists \ tests/test_utils.sh \ tests/security_descriptor_1.base64 \ tests/security_descriptor_1.bin \ tests/security_descriptor_2.base64 \ tests/security_descriptor_2.bin if WINDOWS_NATIVE_BUILD # Tests are run manually for Windows builds. TESTS = else TESTS = $(dist_check_SCRIPTS) endif # Extra test programs (not run by 'make check') EXTRA_PROGRAMS = tests/wlfuzz tests_wlfuzz_SOURCES = tests/wlfuzz.c tests_wlfuzz_LDADD = $(top_builddir)/libwim.la ##############################################################################