| 409 | } |
| 410 | |
| 411 | void getUniqueFileId(HANDLE fd, UCharBuffer& id) |
| 412 | { |
| 413 | entryLoader.init(); |
| 414 | id.clear(); |
| 415 | |
| 416 | // Let's try getting the file identifier. It's not as easy as it may look. |
| 417 | // MSDN says: "After a process opens a file, the identifier is constant until |
| 418 | // the file is closed". So far so good, this is perfectly OK for us. |
| 419 | // But MSDN also says: "An application can use this identifier and the |
| 420 | // volume serial number to determine whether two handles refer to the same file". |
| 421 | // And this part is not true, unfortunately. Volume serial number (VSN) is not |
| 422 | // guaranteed to be unique. It's generated when then volume is formatted and |
| 423 | // it's stored inside the volume's master boot record. But if the volume is cloned |
| 424 | // at the physical block level, or if a virtual (preformatted) drive is used, |
| 425 | // or if volume snapshot is attached as a different logical volume, then VSN may |
| 426 | // duplicate an existing VSN. And we would stay confused thinking that two |
| 427 | // different files are actually the same file. To avoid such a disaster we |
| 428 | // retrieve the final pathname (with symlinks and mount points expanded) |
| 429 | // and extract the volume GUID (for local drives) or its share name |
| 430 | // (for remote drives) as unique volume ID. |
| 431 | |
| 432 | if (fnGetFinalPathNameByHandle) |
| 433 | { |
| 434 | char pathbuf[MAX_PATH + 1]; |
| 435 | DWORD res = fnGetFinalPathNameByHandle(fd, |
| 436 | pathbuf, sizeof(pathbuf), VOLUME_NAME_GUID); |
| 437 | if (res && res < sizeof(pathbuf)) |
| 438 | { |
| 439 | string path(pathbuf); |
| 440 | |
| 441 | // Expected format is \\?\Volume{GUID}\pathname, |
| 442 | // we extract {GUID} and convert into binary format |
| 443 | |
| 444 | const char* const pattern = "\\\\?\\Volume"; |
| 445 | const FB_SIZE_T pos1 = (FB_SIZE_T) strlen(pattern); |
| 446 | |
| 447 | if (path.find(pattern) == 0) |
| 448 | { |
| 449 | const FB_SIZE_T pos2 = path.find_first_of('}'); |
| 450 | |
| 451 | if (path.find_first_of('{') == pos1 && pos2 != string::npos) |
| 452 | { |
| 453 | fb_assert(id.isEmpty()); |
| 454 | id.resize(sizeof(Guid)); |
| 455 | UCHAR* ptr = id.begin(); |
| 456 | bool num_start = true; |
| 457 | |
| 458 | for (FB_SIZE_T n = pos1 + 1; n < pos2 && ptr < id.end(); n++) |
| 459 | { |
| 460 | const char symbol = path[n]; |
| 461 | |
| 462 | if (symbol == '-') |
| 463 | continue; |
| 464 | |
| 465 | fb_assert(isalpha(symbol) || isdigit(symbol)); |
| 466 | |
| 467 | if (symbol >= '0' && symbol <= '9') |
| 468 | *ptr += symbol - '0'; |