Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * UUID generation functions using the BSD, E2FS or OSSP UUID library
4 : : *
5 : : * Copyright (c) 2007-2024, PostgreSQL Global Development Group
6 : : *
7 : : * Portions Copyright (c) 2009 Andrew Gierth
8 : : *
9 : : * contrib/uuid-ossp/uuid-ossp.c
10 : : *
11 : : *-------------------------------------------------------------------------
12 : : */
13 : :
14 : : #include "postgres.h"
15 : :
16 : : #include "common/cryptohash.h"
17 : : #include "common/sha1.h"
18 : : #include "fmgr.h"
19 : : #include "port/pg_bswap.h"
20 : : #include "utils/builtins.h"
21 : : #include "utils/uuid.h"
22 : : #include "varatt.h"
23 : :
24 : : /*
25 : : * It's possible that there's more than one uuid.h header file present.
26 : : * We expect configure to set the HAVE_ symbol for only the one we want.
27 : : *
28 : : * BSD includes a uuid_hash() function that conflicts with the one in
29 : : * builtins.h; we #define it out of the way.
30 : : */
31 : : #define uuid_hash bsd_uuid_hash
32 : :
33 : : #if defined(HAVE_UUID_H)
34 : : #include <uuid.h>
35 : : #elif defined(HAVE_OSSP_UUID_H)
36 : : #include <ossp/uuid.h>
37 : : #elif defined(HAVE_UUID_UUID_H)
38 : : #include <uuid/uuid.h>
39 : : #else
40 : : #error "please use configure's --with-uuid switch to select a UUID library"
41 : : #endif
42 : :
43 : : #undef uuid_hash
44 : :
45 : : /* Check our UUID length against OSSP's; better both be 16 */
46 : : #if defined(HAVE_UUID_OSSP) && (UUID_LEN != UUID_LEN_BIN)
47 : : #error UUID length mismatch
48 : : #endif
49 : :
50 : : /* Define some constants like OSSP's, to make the code more readable */
51 : : #ifndef HAVE_UUID_OSSP
52 : : #define UUID_MAKE_MC 0
53 : : #define UUID_MAKE_V1 1
54 : : #define UUID_MAKE_V2 2
55 : : #define UUID_MAKE_V3 3
56 : : #define UUID_MAKE_V4 4
57 : : #define UUID_MAKE_V5 5
58 : : #endif
59 : :
60 : : /*
61 : : * A DCE 1.1 compatible source representation of UUIDs, derived from
62 : : * the BSD implementation. BSD already has this; OSSP doesn't need it.
63 : : */
64 : : #ifdef HAVE_UUID_E2FS
65 : : typedef struct
66 : : {
67 : : uint32_t time_low;
68 : : uint16_t time_mid;
69 : : uint16_t time_hi_and_version;
70 : : uint8_t clock_seq_hi_and_reserved;
71 : : uint8_t clock_seq_low;
72 : : uint8_t node[6];
73 : : } dce_uuid_t;
74 : : #else
75 : : #define dce_uuid_t uuid_t
76 : : #endif
77 : :
78 : : /* If not OSSP, we need some endianness-manipulation macros */
79 : : #ifndef HAVE_UUID_OSSP
80 : :
81 : : #define UUID_TO_NETWORK(uu) \
82 : : do { \
83 : : uu.time_low = pg_hton32(uu.time_low); \
84 : : uu.time_mid = pg_hton16(uu.time_mid); \
85 : : uu.time_hi_and_version = pg_hton16(uu.time_hi_and_version); \
86 : : } while (0)
87 : :
88 : : #define UUID_TO_LOCAL(uu) \
89 : : do { \
90 : : uu.time_low = pg_ntoh32(uu.time_low); \
91 : : uu.time_mid = pg_ntoh16(uu.time_mid); \
92 : : uu.time_hi_and_version = pg_ntoh16(uu.time_hi_and_version); \
93 : : } while (0)
94 : :
95 : : #define UUID_V3_OR_V5(uu, v) \
96 : : do { \
97 : : uu.time_hi_and_version &= 0x0FFF; \
98 : : uu.time_hi_and_version |= (v << 12); \
99 : : uu.clock_seq_hi_and_reserved &= 0x3F; \
100 : : uu.clock_seq_hi_and_reserved |= 0x80; \
101 : : } while(0)
102 : :
103 : : #endif /* !HAVE_UUID_OSSP */
104 : :
6203 peter_e@gmx.net 105 :CBC 1 : PG_MODULE_MAGIC;
106 : :
107 : 2 : PG_FUNCTION_INFO_V1(uuid_nil);
108 : 2 : PG_FUNCTION_INFO_V1(uuid_ns_dns);
109 : 2 : PG_FUNCTION_INFO_V1(uuid_ns_url);
110 : 2 : PG_FUNCTION_INFO_V1(uuid_ns_oid);
111 : 2 : PG_FUNCTION_INFO_V1(uuid_ns_x500);
112 : :
113 : 2 : PG_FUNCTION_INFO_V1(uuid_generate_v1);
114 : 2 : PG_FUNCTION_INFO_V1(uuid_generate_v1mc);
115 : 2 : PG_FUNCTION_INFO_V1(uuid_generate_v3);
116 : 2 : PG_FUNCTION_INFO_V1(uuid_generate_v4);
117 : 2 : PG_FUNCTION_INFO_V1(uuid_generate_v5);
118 : :
119 : : #ifdef HAVE_UUID_OSSP
120 : :
121 : : static void
122 : : pguuid_complain(uuid_rc_t rc)
123 : : {
124 : : char *err = uuid_error(rc);
125 : :
126 : : if (err != NULL)
127 : : ereport(ERROR,
128 : : (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
129 : : errmsg("OSSP uuid library failure: %s", err)));
130 : : else
131 : : ereport(ERROR,
132 : : (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
133 : : errmsg("OSSP uuid library failure: error code %d", rc)));
134 : : }
135 : :
136 : : /*
137 : : * We create a uuid_t object just once per session and re-use it for all
138 : : * operations in this module. OSSP UUID caches the system MAC address and
139 : : * other state in this object. Reusing the object has a number of benefits:
140 : : * saving the cycles needed to fetch the system MAC address over and over,
141 : : * reducing the amount of entropy we draw from /dev/urandom, and providing a
142 : : * positive guarantee that successive generated V1-style UUIDs don't collide.
143 : : * (On a machine fast enough to generate multiple UUIDs per microsecond,
144 : : * or whatever the system's wall-clock resolution is, we'd otherwise risk
145 : : * collisions whenever random initialization of the uuid_t's clock sequence
146 : : * value chanced to produce duplicates.)
147 : : *
148 : : * However: when we're doing V3 or V5 UUID creation, uuid_make needs two
149 : : * uuid_t objects, one holding the namespace UUID and one for the result.
150 : : * It's unspecified whether it's safe to use the same uuid_t for both cases,
151 : : * so let's cache a second uuid_t for use as the namespace holder object.
152 : : */
153 : : static uuid_t *
154 : : get_cached_uuid_t(int which)
155 : : {
156 : : static uuid_t *cached_uuid[2] = {NULL, NULL};
157 : :
158 : : if (cached_uuid[which] == NULL)
159 : : {
160 : : uuid_rc_t rc;
161 : :
162 : : rc = uuid_create(&cached_uuid[which]);
163 : : if (rc != UUID_RC_OK)
164 : : {
165 : : cached_uuid[which] = NULL;
166 : : pguuid_complain(rc);
167 : : }
168 : : }
169 : : return cached_uuid[which];
170 : : }
171 : :
172 : : static char *
173 : : uuid_to_string(const uuid_t *uuid)
174 : : {
175 : : char *buf = palloc(UUID_LEN_STR + 1);
176 : : void *ptr = buf;
177 : : size_t len = UUID_LEN_STR + 1;
178 : : uuid_rc_t rc;
179 : :
180 : : rc = uuid_export(uuid, UUID_FMT_STR, &ptr, &len);
181 : : if (rc != UUID_RC_OK)
182 : : pguuid_complain(rc);
183 : :
184 : : return buf;
185 : : }
186 : :
187 : :
188 : : static void
189 : : string_to_uuid(const char *str, uuid_t *uuid)
190 : : {
191 : : uuid_rc_t rc;
192 : :
193 : : rc = uuid_import(uuid, UUID_FMT_STR, str, UUID_LEN_STR + 1);
194 : : if (rc != UUID_RC_OK)
195 : : pguuid_complain(rc);
196 : : }
197 : :
198 : :
199 : : static Datum
200 : : special_uuid_value(const char *name)
201 : : {
202 : : uuid_t *uuid = get_cached_uuid_t(0);
203 : : char *str;
204 : : uuid_rc_t rc;
205 : :
206 : : rc = uuid_load(uuid, name);
207 : : if (rc != UUID_RC_OK)
208 : : pguuid_complain(rc);
209 : : str = uuid_to_string(uuid);
210 : :
211 : : return DirectFunctionCall1(uuid_in, CStringGetDatum(str));
212 : : }
213 : :
214 : : /* len is unused with OSSP, but we want to have the same number of args */
215 : : static Datum
216 : : uuid_generate_internal(int mode, const uuid_t *ns, const char *name, int len)
217 : : {
218 : : uuid_t *uuid = get_cached_uuid_t(0);
219 : : char *str;
220 : : uuid_rc_t rc;
221 : :
222 : : rc = uuid_make(uuid, mode, ns, name);
223 : : if (rc != UUID_RC_OK)
224 : : pguuid_complain(rc);
225 : : str = uuid_to_string(uuid);
226 : :
227 : : return DirectFunctionCall1(uuid_in, CStringGetDatum(str));
228 : : }
229 : :
230 : :
231 : : static Datum
232 : : uuid_generate_v35_internal(int mode, pg_uuid_t *ns, text *name)
233 : : {
234 : : uuid_t *ns_uuid = get_cached_uuid_t(1);
235 : :
236 : : string_to_uuid(DatumGetCString(DirectFunctionCall1(uuid_out,
237 : : UUIDPGetDatum(ns))),
238 : : ns_uuid);
239 : :
240 : : return uuid_generate_internal(mode,
241 : : ns_uuid,
242 : : text_to_cstring(name),
243 : : 0);
244 : : }
245 : :
246 : : #else /* !HAVE_UUID_OSSP */
247 : :
248 : : static Datum
2357 249 : 30 : uuid_generate_internal(int v, unsigned char *ns, const char *ptr, int len)
250 : : {
251 : : char strbuf[40];
252 : :
3610 tgl@sss.pgh.pa.us 253 [ + + + + ]: 30 : switch (v)
254 : : {
2489 255 : 7 : case 0: /* constant-value uuids */
3610 256 : 7 : strlcpy(strbuf, ptr, 37);
257 : 7 : break;
258 : :
2489 259 : 17 : case 1: /* time/node-based uuids */
260 : : {
261 : : #ifdef HAVE_UUID_E2FS
262 : : uuid_t uu;
263 : :
3610 264 : 17 : uuid_generate_time(uu);
265 : 17 : uuid_unparse(uu, strbuf);
266 : :
267 : : /*
268 : : * PTR, if set, replaces the trailing characters of the uuid;
269 : : * this is to support v1mc, where a random multicast MAC is
270 : : * used instead of the physical one
271 : : */
272 [ + + + - ]: 17 : if (ptr && len <= 36)
273 : 9 : strcpy(strbuf + (36 - len), ptr);
274 : : #else /* BSD */
275 : : uuid_t uu;
276 : : uint32_t status = uuid_s_ok;
277 : : char *str = NULL;
278 : :
279 : : uuid_create(&uu, &status);
280 : :
281 : : if (status == uuid_s_ok)
282 : : {
283 : : uuid_to_string(&uu, &str, &status);
284 : : if (status == uuid_s_ok)
285 : : {
286 : : strlcpy(strbuf, str, 37);
287 : :
288 : : /*
289 : : * In recent NetBSD, uuid_create() has started
290 : : * producing v4 instead of v1 UUIDs. Check the
291 : : * version field and complain if it's not v1.
292 : : */
293 : : if (strbuf[14] != '1')
294 : : ereport(ERROR,
295 : : (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
296 : : /* translator: %c will be a hex digit */
297 : : errmsg("uuid_create() produced a version %c UUID instead of the expected version 1",
298 : : strbuf[14])));
299 : :
300 : : /*
301 : : * PTR, if set, replaces the trailing characters of
302 : : * the uuid; this is to support v1mc, where a random
303 : : * multicast MAC is used instead of the physical one
304 : : */
305 : : if (ptr && len <= 36)
306 : : strcpy(strbuf + (36 - len), ptr);
307 : : }
308 : : free(str);
309 : : }
310 : :
311 : : if (status != uuid_s_ok)
312 : : ereport(ERROR,
313 : : (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
314 : : errmsg("uuid library failure: %d",
315 : : (int) status)));
316 : : #endif
317 : 17 : break;
318 : : }
319 : :
2489 320 : 2 : case 3: /* namespace-based MD5 uuids */
321 : : case 5: /* namespace-based SHA1 uuids */
322 : : {
323 : : dce_uuid_t uu;
324 : : #ifdef HAVE_UUID_BSD
325 : : uint32_t status = uuid_s_ok;
326 : : char *str = NULL;
327 : : #endif
328 : :
3610 329 [ + + ]: 2 : if (v == 3)
330 : : {
1221 michael@paquier.xyz 331 : 1 : pg_cryptohash_ctx *ctx = pg_cryptohash_create(PG_MD5);
332 : :
333 [ - + ]: 1 : if (pg_cryptohash_init(ctx) < 0)
824 michael@paquier.xyz 334 [ # # ]:UBC 0 : elog(ERROR, "could not initialize %s context: %s", "MD5",
335 : : pg_cryptohash_error(ctx));
1221 michael@paquier.xyz 336 [ + - - + ]:CBC 2 : if (pg_cryptohash_update(ctx, ns, sizeof(uu)) < 0 ||
337 : 1 : pg_cryptohash_update(ctx, (unsigned char *) ptr, len) < 0)
824 michael@paquier.xyz 338 [ # # ]:UBC 0 : elog(ERROR, "could not update %s context: %s", "MD5",
339 : : pg_cryptohash_error(ctx));
340 : : /* we assume sizeof MD5 result is 16, same as UUID size */
1154 michael@paquier.xyz 341 [ - + ]:CBC 1 : if (pg_cryptohash_final(ctx, (unsigned char *) &uu,
342 : : sizeof(uu)) < 0)
824 michael@paquier.xyz 343 [ # # ]:UBC 0 : elog(ERROR, "could not finalize %s context: %s", "MD5",
344 : : pg_cryptohash_error(ctx));
1221 michael@paquier.xyz 345 :CBC 1 : pg_cryptohash_free(ctx);
346 : : }
347 : : else
348 : : {
1177 349 : 1 : pg_cryptohash_ctx *ctx = pg_cryptohash_create(PG_SHA1);
350 : : unsigned char sha1result[SHA1_DIGEST_LENGTH];
351 : :
352 [ - + ]: 1 : if (pg_cryptohash_init(ctx) < 0)
824 michael@paquier.xyz 353 [ # # ]:UBC 0 : elog(ERROR, "could not initialize %s context: %s", "SHA1",
354 : : pg_cryptohash_error(ctx));
1177 michael@paquier.xyz 355 [ + - - + ]:CBC 2 : if (pg_cryptohash_update(ctx, ns, sizeof(uu)) < 0 ||
356 : 1 : pg_cryptohash_update(ctx, (unsigned char *) ptr, len) < 0)
824 michael@paquier.xyz 357 [ # # ]:UBC 0 : elog(ERROR, "could not update %s context: %s", "SHA1",
358 : : pg_cryptohash_error(ctx));
1154 michael@paquier.xyz 359 [ - + ]:CBC 1 : if (pg_cryptohash_final(ctx, sha1result, sizeof(sha1result)) < 0)
824 michael@paquier.xyz 360 [ # # ]:UBC 0 : elog(ERROR, "could not finalize %s context: %s", "SHA1",
361 : : pg_cryptohash_error(ctx));
1177 michael@paquier.xyz 362 :CBC 1 : pg_cryptohash_free(ctx);
363 : :
3609 tgl@sss.pgh.pa.us 364 : 1 : memcpy(&uu, sha1result, sizeof(uu));
365 : : }
366 : :
367 : : /* the calculated hash is using local order */
3610 368 : 2 : UUID_TO_NETWORK(uu);
369 : 2 : UUID_V3_OR_V5(uu, v);
370 : :
371 : : #ifdef HAVE_UUID_E2FS
372 : : /* uuid_unparse expects local order */
373 : 2 : UUID_TO_LOCAL(uu);
374 : 2 : uuid_unparse((unsigned char *) &uu, strbuf);
375 : : #else /* BSD */
376 : : uuid_to_string(&uu, &str, &status);
377 : :
378 : : if (status == uuid_s_ok)
379 : : strlcpy(strbuf, str, 37);
380 : :
381 : : free(str);
382 : :
383 : : if (status != uuid_s_ok)
384 : : ereport(ERROR,
385 : : (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
386 : : errmsg("uuid library failure: %d",
387 : : (int) status)));
388 : : #endif
389 : 2 : break;
390 : : }
391 : :
2489 392 : 4 : case 4: /* random uuid */
393 : : default:
394 : : {
395 : : #ifdef HAVE_UUID_E2FS
396 : : uuid_t uu;
397 : :
3610 398 : 4 : uuid_generate_random(uu);
399 : 4 : uuid_unparse(uu, strbuf);
400 : : #else /* BSD */
401 : : snprintf(strbuf, sizeof(strbuf),
402 : : "%08lx-%04x-%04x-%04x-%04x%08lx",
403 : : (unsigned long) arc4random(),
404 : : (unsigned) (arc4random() & 0xffff),
405 : : (unsigned) ((arc4random() & 0xfff) | 0x4000),
406 : : (unsigned) ((arc4random() & 0x3fff) | 0x8000),
407 : : (unsigned) (arc4random() & 0xffff),
408 : : (unsigned long) arc4random());
409 : : #endif
410 : 4 : break;
411 : : }
412 : : }
413 : :
414 : 30 : return DirectFunctionCall1(uuid_in, CStringGetDatum(strbuf));
415 : : }
416 : :
417 : : #endif /* HAVE_UUID_OSSP */
418 : :
419 : :
420 : : Datum
6203 peter_e@gmx.net 421 : 1 : uuid_nil(PG_FUNCTION_ARGS)
422 : : {
423 : : #ifdef HAVE_UUID_OSSP
424 : : return special_uuid_value("nil");
425 : : #else
3610 tgl@sss.pgh.pa.us 426 : 1 : return uuid_generate_internal(0, NULL,
427 : : "00000000-0000-0000-0000-000000000000", 36);
428 : : #endif
429 : : }
430 : :
431 : :
432 : : Datum
6203 peter_e@gmx.net 433 : 3 : uuid_ns_dns(PG_FUNCTION_ARGS)
434 : : {
435 : : #ifdef HAVE_UUID_OSSP
436 : : return special_uuid_value("ns:DNS");
437 : : #else
3610 tgl@sss.pgh.pa.us 438 : 3 : return uuid_generate_internal(0, NULL,
439 : : "6ba7b810-9dad-11d1-80b4-00c04fd430c8", 36);
440 : : #endif
441 : : }
442 : :
443 : :
444 : : Datum
6203 peter_e@gmx.net 445 : 1 : uuid_ns_url(PG_FUNCTION_ARGS)
446 : : {
447 : : #ifdef HAVE_UUID_OSSP
448 : : return special_uuid_value("ns:URL");
449 : : #else
3610 tgl@sss.pgh.pa.us 450 : 1 : return uuid_generate_internal(0, NULL,
451 : : "6ba7b811-9dad-11d1-80b4-00c04fd430c8", 36);
452 : : #endif
453 : : }
454 : :
455 : :
456 : : Datum
6203 peter_e@gmx.net 457 : 1 : uuid_ns_oid(PG_FUNCTION_ARGS)
458 : : {
459 : : #ifdef HAVE_UUID_OSSP
460 : : return special_uuid_value("ns:OID");
461 : : #else
3610 tgl@sss.pgh.pa.us 462 : 1 : return uuid_generate_internal(0, NULL,
463 : : "6ba7b812-9dad-11d1-80b4-00c04fd430c8", 36);
464 : : #endif
465 : : }
466 : :
467 : :
468 : : Datum
6203 peter_e@gmx.net 469 : 1 : uuid_ns_x500(PG_FUNCTION_ARGS)
470 : : {
471 : : #ifdef HAVE_UUID_OSSP
472 : : return special_uuid_value("ns:X500");
473 : : #else
3610 tgl@sss.pgh.pa.us 474 : 1 : return uuid_generate_internal(0, NULL,
475 : : "6ba7b814-9dad-11d1-80b4-00c04fd430c8", 36);
476 : : #endif
477 : : }
478 : :
479 : :
480 : : Datum
6203 peter_e@gmx.net 481 : 8 : uuid_generate_v1(PG_FUNCTION_ARGS)
482 : : {
3610 tgl@sss.pgh.pa.us 483 : 8 : return uuid_generate_internal(UUID_MAKE_V1, NULL, NULL, 0);
484 : : }
485 : :
486 : :
487 : : Datum
6203 peter_e@gmx.net 488 : 9 : uuid_generate_v1mc(PG_FUNCTION_ARGS)
489 : : {
490 : : #ifdef HAVE_UUID_OSSP
491 : : char *buf = NULL;
492 : : #elif defined(HAVE_UUID_E2FS)
493 : : char strbuf[40];
494 : : char *buf;
495 : : uuid_t uu;
496 : :
3610 tgl@sss.pgh.pa.us 497 : 9 : uuid_generate_random(uu);
498 : :
499 : : /* set IEEE802 multicast and local-admin bits */
500 : 9 : ((dce_uuid_t *) &uu)->node[0] |= 0x03;
501 : :
502 : 9 : uuid_unparse(uu, strbuf);
503 : 9 : buf = strbuf + 24;
504 : : #else /* BSD */
505 : : char buf[16];
506 : :
507 : : /* set IEEE802 multicast and local-admin bits */
508 : : snprintf(buf, sizeof(buf), "-%04x%08lx",
509 : : (unsigned) ((arc4random() & 0xffff) | 0x0300),
510 : : (unsigned long) arc4random());
511 : : #endif
512 : :
513 : 9 : return uuid_generate_internal(UUID_MAKE_V1 | UUID_MAKE_MC, NULL,
514 : : buf, 13);
515 : : }
516 : :
517 : :
518 : : Datum
6203 peter_e@gmx.net 519 : 1 : uuid_generate_v3(PG_FUNCTION_ARGS)
520 : : {
521 : 1 : pg_uuid_t *ns = PG_GETARG_UUID_P(0);
2590 noah@leadboat.com 522 : 1 : text *name = PG_GETARG_TEXT_PP(1);
523 : :
524 : : #ifdef HAVE_UUID_OSSP
525 : : return uuid_generate_v35_internal(UUID_MAKE_V3, ns, name);
526 : : #else
3610 tgl@sss.pgh.pa.us 527 :UBC 0 : return uuid_generate_internal(UUID_MAKE_V3, (unsigned char *) ns,
2590 noah@leadboat.com 528 [ - + - - :CBC 2 : VARDATA_ANY(name), VARSIZE_ANY_EXHDR(name));
- - - - -
+ - + ]
529 : : #endif
530 : : }
531 : :
532 : :
533 : : Datum
6203 peter_e@gmx.net 534 : 4 : uuid_generate_v4(PG_FUNCTION_ARGS)
535 : : {
3610 tgl@sss.pgh.pa.us 536 : 4 : return uuid_generate_internal(UUID_MAKE_V4, NULL, NULL, 0);
537 : : }
538 : :
539 : :
540 : : Datum
6203 peter_e@gmx.net 541 : 1 : uuid_generate_v5(PG_FUNCTION_ARGS)
542 : : {
543 : 1 : pg_uuid_t *ns = PG_GETARG_UUID_P(0);
2590 noah@leadboat.com 544 : 1 : text *name = PG_GETARG_TEXT_PP(1);
545 : :
546 : : #ifdef HAVE_UUID_OSSP
547 : : return uuid_generate_v35_internal(UUID_MAKE_V5, ns, name);
548 : : #else
3610 tgl@sss.pgh.pa.us 549 :UBC 0 : return uuid_generate_internal(UUID_MAKE_V5, (unsigned char *) ns,
2590 noah@leadboat.com 550 [ - + - - :CBC 2 : VARDATA_ANY(name), VARSIZE_ANY_EXHDR(name));
- - - - -
+ - + ]
551 : : #endif
552 : : }
|