#include #include #include struct handle_wrapper { HANDLE handle; handle_wrapper() : handle(INVALID_HANDLE_VALUE) {} ~handle_wrapper() { if ( handle != INVALID_HANDLE_VALUE ) ::CloseHandle(handle); } }; void print_error(char const *what, int code, char const *path = 0) { std::cout << "error: " << what << '\n'; if(path) std::cout << "error with path: " << path << '\n'; std::cout << "error code: " << code << '\n'; char *message_buffer; if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast(&message_buffer), 0, 0)) { std::cout << "error message: " << message_buffer << '\n'; LocalFree(message_buffer); } std::cout << std::flush; } class fs_error : public std::exception { int code_; char const *path_; char const *what_; public: fs_error(char const *what, int code, char const *path = 0) : what_(what), code_(code), path_(path) {} int code() const { return code_; } char const *path() const { return path_; } char const *what() const throw () { return what_; } }; bool equivalent(char const *ph1, char const *ph2) { bool ph1_is_directory, ph2_is_directory; DWORD attributes; attributes = ::GetFileAttributesA(ph1); if(attributes == INVALID_FILE_ATTRIBUTES) throw fs_error("equivalent: GetFileAttributes", ::GetLastError(), ph1); ph1_is_directory = attributes & FILE_ATTRIBUTE_DIRECTORY; attributes = ::GetFileAttributesA(ph2); if(attributes == INVALID_FILE_ATTRIBUTES) throw fs_error("equivalent: GetFileAttributes", ::GetLastError(), ph2); ph2_is_directory = attributes & FILE_ATTRIBUTE_DIRECTORY; if(ph1_is_directory != ph2_is_directory) return false; DWORD flags = ph1_is_directory ? FILE_FLAG_BACKUP_SEMANTICS : 0; handle_wrapper file1, file2; if ( (file1.handle = ::CreateFileA(ph1, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, flags, 0)) == INVALID_HANDLE_VALUE ) throw fs_error("equivalent: CreateFile", ::GetLastError(), ph1); if ( (file2.handle = ::CreateFileA(ph2, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, flags, 0)) == INVALID_HANDLE_VALUE ) throw fs_error("equivalent: CreateFile", ::GetLastError(), ph2); BY_HANDLE_FILE_INFORMATION info1, info2; if ( !::GetFileInformationByHandle(file1.handle, &info1) ) throw fs_error("equivalent: GetFileInformationByHandle", ::GetLastError(), ph1); if ( !::GetFileInformationByHandle(file2.handle, &info2) ) throw fs_error("equivalent: GetFileInformationByHandle", ::GetLastError(), ph2); return info1.dwVolumeSerialNumber == info2.dwVolumeSerialNumber && info1.nFileIndexHigh == info2.nFileIndexHigh && info1.nFileIndexLow == info2.nFileIndexLow; } int main(int argc, char **argv) { if(argc != 3) { std::cout << "Exactly two arguments are required.\n\n" "usage: " << (argc > 0 ? argv[0] : "file-equivalence-win32") << " " << std::endl; return 1; } try { if(equivalent(argv[1], argv[2])) std::cout << "same" << std::endl; else std::cout << "different" << std::endl; } catch(fs_error &e) { print_error(e.what(), e.code(), e.path()); } catch(std::exception &e) { std::cout << "exception: " << e.what() << std::endl; return 1; } }