Создание hardlink и symbolic linkDelphi , Файловая система , ЯрлыкиСоздание hardlink и symbolic link
Автор: Alex Konshin { **** UBPFD *********** by delphibase.endimus.com **** >> Создание hardlink и symbolic link. Исходный код утилиты, которая создает hard и symbolic links почти как в unix. Hardlink можно создать только для файлов и только на NTFS. Symbolic link можно создать только для директориев и только на NTFS5 (Win2K/XP) и он не может указывать на сетевой ресурс. Зависимости: Windows, SysUtils Автор: Alex Konshin, akonshin@earthlink.net, Boston, USA Copyright: http://home.earthlink.net/~akonshin/files/xlink.zip Дата: 30 декабря 2002 г. ***************************************************** } program xlink; uses Windows, SysUtils; {$APPTYPE CONSOLE} {$R xlink.res} type TOptions = set of (optSymbolicLink, optOverwrite, optRecursive, optDirectory); int64rec = packed record lo: LongWord; hi: LongInt; end; const FILE_DOES_NOT_EXIST = DWORD(-1); //============================================================= function isFileExists(const AFileName: string): Boolean; var h: THandle; rFindData: TWin32FindData; begin h := Windows.FindFirstFile(PChar(AFileName), rFindData); Result := h <> INVALID_HANDLE_VALUE; if not Result then Exit; Windows.FindClose(h); Result := (rFindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0; end; //------------------------------------------------------------- // warning: function assumes that it is correct directory name function isDirectoryEmpty(const ADirectoryName: string): Boolean; var h: THandle; len: Integer; rFindData: TWin32FindData; sSeachMask: string; begin len := Length(ADirectoryName); if (PChar(ADirectoryName) + len - 1)^ = '\' then sSeachMask := ADirectoryName + '*' else sSeachMask := ADirectoryName + '\*'; h := Windows.FindFirstFile(PChar(sSeachMask), rFindData); Result := (h = INVALID_HANDLE_VALUE); Windows.FindClose(h); end; //------------------------------------------------------------- function SysErrorMessage(ErrorCode: Integer): string; var Len: Integer; Buffer: array[0..255] of Char; begin Len := FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_ARGUMENT_ARRAY, nil, ErrorCode, 0, Buffer, SizeOf(Buffer), nil); while (Len > 0) and (Buffer[Len - 1] in [#0..' ', '.']) do Dec(Len); SetString(Result, Buffer, Len); end; //------------------------------------------------------------- procedure _CreateHardlink(AFileName: string; AFileWCName: PWideChar; ALinkName: string; overwrite: Boolean); var aLinkWCFileName, aLinkFullName: array[0..MAX_PATH] of WChar; pwFilePart: LPWSTR; hFileSource: THandle; rStreamId: WIN32_STREAM_ID; cbPathLen, dwStreamHeaderSize, dwBytesWritten: DWORD; lpContext: Pointer; begin StringToWidechar(ALinkName, aLinkWCFileName, MAX_PATH); hFileSource := Windows.CreateFile( PChar(AFileName), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE, nil, OPEN_EXISTING, 0, 0 ); if hFileSource = INVALID_HANDLE_VALUE then raise Exception.Create('Can''t open file "' + AFileName + '"'); try cbPathLen := Windows.GetFullPathNameW(aLinkWCFileName, MAX_PATH, aLinkFullName, pwFilePart); if cbPathLen <= 0 then raise Exception.Create('Invalid link name "' + ALinkName + '"'); cbPathLen := (cbPathLen + 1) * SizeOf(WChar); lpContext := nil; rStreamId.dwStreamId := BACKUP_LINK; rStreamId.dwStreamAttributes := 0; rStreamId.dwStreamNameSize := 0; int64rec(rStreamId.Size).hi := 0; int64rec(rStreamId.Size).lo := cbPathLen; dwStreamHeaderSize := PChar(@rStreamId.cStreamName) - PChar(@rStreamId) + LongInt(rStreamId.dwStreamNameSize); if not BackupWrite( hFileSource, Pointer(@rStreamId), // buffer to write dwStreamHeaderSize, // number of bytes to write dwBytesWritten, False, // don't abort yet False, // don't process security lpContext ) then RaiseLastOSError; if not BackupWrite( hFileSource, Pointer(@aLinkFullName), // buffer to write cbPathLen, // number of bytes to write dwBytesWritten, False, // don't abort yet False, // don't process security lpContext ) then RaiseLastOSError; // free context if not BackupWrite( hFileSource, nil, // buffer to write 0, // number of bytes to write dwBytesWritten, True, // abort False, // don't process security lpContext ) then RaiseLastOSError; finally CloseHandle(hFileSource); end; end; //------------------------------------------------------------- // ADirName and ADirForLinks must not end with backslach procedure _CreateHardlinksForSubDirectory(const ADirName, ADirForLinks: string; options: TOptions); var h: THandle; sExistedFile, sLinkName: string; dwAttributes: DWORD; rFindData: TWin32FindData; awcFileName: array[0..MAX_PATH] of WChar; begin dwAttributes := GetFileAttributes(PChar(ADirForLinks)); if dwAttributes = FILE_DOES_NOT_EXIST then begin // WriteLn('Create Directory ',ADirForLinks); if not CreateDir(ADirForLinks) then raise Exception.Create('Can''t create directory "' + ADirForLinks + '".'); end else if (dwAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then raise Exception.Create('File "' + ADirName + '" already exists and it is not a directory.'); h := Windows.FindFirstFile(PChar(ADirName + '\*'), rFindData); if h = INVALID_HANDLE_VALUE then Exit; try repeat if (rFindData.cFileName[0] = '.') and ((rFindData.cFileName[1] = #0) or ((rFindData.cFileName[1] = '.') and (rFindData.cFileName[2] = #0))) then Continue; sExistedFile := ADirName + '\' + rFindData.cFileName; sLinkName := ADirForLinks + '\' + rFindData.cFileName; if (rFindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then begin awcFileName[ Windows.MultiByteToWideChar(0, 0, PChar(sExistedFile), MAX_PATH, awcFileName, MAX_PATH) ] := #0; _CreateHardlink(sExistedFile, awcFileName, sLinkName, optOverwrite in options); end else if optRecursive in options then begin _CreateHardlinksForSubDirectory(sExistedFile, sLinkName, options); end; until not Windows.FindNextFile(h, rFindData); finally Windows.FindClose(h); end; end; //------------------------------------------------------------- procedure CreateHardlink(AFileName, ALinkName: string; options: TOptions); var dwAttributes: DWORD; aFileSource: array[0..MAX_PATH] of WChar; begin dwAttributes := Windows.GetFileAttributes(PChar(AFileName)); if dwAttributes = FILE_DOES_NOT_EXIST then raise Exception.Create('File "' + AFileName + '" does not exist.'); if (dwAttributes and FILE_ATTRIBUTE_DIRECTORY) <> 0 then raise Exception.Create('Can''t create hardlink for directory (file "' + AFileName + '").'); dwAttributes := Windows.GetFileAttributes(PChar(ALinkName)); if dwAttributes <> FILE_DOES_NOT_EXIST then begin if not (optOverwrite in options) then raise Exception.Create('File "' + ALinkName + '" already exists.'); if (dwAttributes and FILE_ATTRIBUTE_DIRECTORY) <> 0 then raise Exception.Create('Can''t overwrite directory "' + AFileName + '".'); end; StringToWidechar(AFileName, aFileSource, MAX_PATH); _CreateHardlink(AFileName, aFileSource, ALinkName, optOverwrite in options); end; //------------------------------------------------------------- procedure CreateHardlinksForDirectory(const ADirName, ADirForLinks: string; options: TOptions); var dwAttributes: DWORD; len: Integer; sDirName, sDirForLinks: string; begin dwAttributes := Windows.GetFileAttributes(PChar(ADirName)); if dwAttributes = FILE_DOES_NOT_EXIST then raise Exception.Create('Directory "' + ADirName + '" does not exist.'); if (dwAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then raise Exception.Create('File "' + ADirName + '" is not a directory.'); len := Length(ADirName); if (PChar(ADirName) + len - 1)^ = '\' then sDirName := Copy(ADirName, 1, len - 1) else sDirName := ADirName; if (PChar(ADirForLinks) + Length(ADirForLinks) - 1)^ <> '\' then sDirForLinks := ADirForLinks else sDirForLinks := Copy(ADirForLinks, 1, Length(ADirForLinks) - 1); _CreateHardlinksForSubDirectory(sDirName, sDirForLinks, options); end; //------------------------------------------------------------- procedure CreateHardlinksInDirectory(const AFileName, ADirForLinks: string; options: TOptions); var dwAttributes: DWORD; len: Integer; sFileName, sDirForLinks, sLinkName: string; aFileSource: array[0..MAX_PATH] of WChar; begin dwAttributes := Windows.GetFileAttributes(PChar(AFileName)); if dwAttributes = FILE_DOES_NOT_EXIST then raise Exception.Create('File or directory "' + AFileName + '" does not exist.'); if (dwAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then begin sLinkName := ADirForLinks + '\' + SysUtils.ExpandFileName(AFileName); dwAttributes := Windows.GetFileAttributes(PChar(sLinkName)); if dwAttributes <> FILE_DOES_NOT_EXIST then begin if not (optOverwrite in options) then raise Exception.Create('File "' + sLinkName + '" already exists.'); if (dwAttributes and FILE_ATTRIBUTE_DIRECTORY) <> 0 then raise Exception.Create('Can''t overwrite directory "' + AFileName + '".'); end; StringToWidechar(AFileName, aFileSource, MAX_PATH); _CreateHardlink(AFileName, aFileSource, sLinkName, optOverwrite in options); end else begin len := Length(AFileName); if (PChar(AFileName) + len - 1)^ = '\' then sFileName := Copy(AFileName, 1, len - 1) else sFileName := AFileName; if (PChar(ADirForLinks) + Length(ADirForLinks) - 1)^ <> '\' then sDirForLinks := ADirForLinks else sDirForLinks := Copy(ADirForLinks, 1, Length(ADirForLinks) - 1); _CreateHardlinksForSubDirectory(sFileName, sDirForLinks, options); end; end; //------------------------------------------------------------- procedure DeleteDirectoryContent(const ADirName: string); type PDirRef = ^TDirRef; PPDirRef = ^PDirRef; TDirRef = record Next: PDirRef; DirName: string; end; var h: THandle; sFileName: string; pSubDirs: PDirRef; ppLast: PPDirRef; pDir: PDirRef; rFindData: TWin32FindData; begin pSubDirs := nil; ppLast := @pSubDirs; h := Windows.FindFirstFile(PChar(ADirName + '\*'), rFindData); if h = INVALID_HANDLE_VALUE then Exit; try try repeat if (rFindData.cFileName[0] = '.') and ((rFindData.cFileName[1] = #0) or ((rFindData.cFileName[1] = '.') and (rFindData.cFileName[2] = #0))) then Continue; sFileName := ADirName + '\' + rFindData.cFileName; if (rFindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) <> 0 then begin New(pDir); with pDir^ do begin Next := nil; DirName := sFileName; end; ppLast^ := pDir; ppLast := @pDir^.Next; end else if not DeleteFile(sFileName) then raise Exception.Create('Can''t delete file "' + sFileName + '".'); until not Windows.FindNextFile(h, rFindData); finally Windows.FindClose(h); end; if pSubDirs <> nil then begin repeat pDir := pSubDirs; pSubDirs := pDir^.Next; sFileName := pDir^.DirName; Dispose(pDir); DeleteDirectoryContent(sFileName); if not RemoveDir(sFileName) then raise Exception.Create('Can''t delete directory "' + sFileName + '".'); until pSubDirs = nil; end; except while pSubDirs <> nil do begin pDir := pSubDirs; pSubDirs := pDir^.Next; Dispose(pDir); end; raise; end; end; //------------------------------------------------------------- const FILE_DEVICE_FILE_SYSTEM = $0009; // Define the method codes for how buffers are passed for I/O and FS controls METHOD_BUFFERED = 0; METHOD_IN_DIRECT = 1; METHOD_OUT_DIRECT = 2; METHOD_NEITHER = 3; // Define the access check value for any access FILE_ANY_ACCESS = 0; FILE_READ_DATA = 1; FILE_WRITE_DATA = 2; FSCTL_SET_REPARSE_POINT = (FILE_DEVICE_FILE_SYSTEM shl 16) or (FILE_ANY_ACCESS shl 14) or (41 shl 2) or (METHOD_BUFFERED); FSCTL_GET_REPARSE_POINT = (FILE_DEVICE_FILE_SYSTEM shl 16) or (FILE_ANY_ACCESS shl 14) or (42 shl 2) or (METHOD_BUFFERED); FSCTL_DELETE_REPARSE_POINT = (FILE_DEVICE_FILE_SYSTEM shl 16) or (FILE_ANY_ACCESS shl 14) or (43 shl 2) or (METHOD_BUFFERED); FILE_FLAG_OPEN_REPARSE_POINT = $00200000; FILE_ATTRIBUTE_REPARSE_POINT = $00000400; IO_REPARSE_TAG_MOUNT_POINT = $A0000003; REPARSE_MOUNTPOINT_HEADER_SIZE = 8; type REPARSE_MOUNTPOINT_DATA_BUFFER = packed record ReparseTag: DWORD; ReparseDataLength: DWORD; Reserved: Word; ReparseTargetLength: Word; ReparseTargetMaximumLength: Word; Reserved1: Word; ReparseTarget: array[0..0] of WChar; end; TReparseMountpointDataBuffer = REPARSE_MOUNTPOINT_DATA_BUFFER; PReparseMountpointDataBuffer = ^TReparseMountpointDataBuffer; //------------------------------------------------------------- function CreateSymlink(ATargetName, ALinkName: string; const options: TOptions): Boolean; const pwcNativeFileNamePrefix: PWideChar = '\??\'; nNativeFileNamePrefixWCharLength = 4; nNativeFileNamePrefixByteLength = nNativeFileNamePrefixWCharLength * 2; var hLink: THandle; pReparseInfo: PReparseMountpointDataBuffer; len, size: Integer; pwcLinkFileName: PWideChar; pwcTargetNativeFileName: PWideChar; pwcTargetFileName: PWideChar; pwc: PWideChar; pc: PChar; dwBytesReturned: DWORD; dwAttributes: DWORD; bDirectoryCreated: Boolean; aTargetFullName: array[0..MAX_PATH] of Char; begin Result := False; pReparseInfo := nil; hLink := INVALID_HANDLE_VALUE; bDirectoryCreated := False; len := Length(ALinkName); if ((PChar(ALinkName) + len - 1)^ = '\') and ((PChar(ALinkName) + len - 2)^ <> ':') then begin Dec(len); SetLength(ALinkName, len); end; System.GetMem(pwcLinkFileName, len + len + 2); try pwcLinkFileName[ Windows.MultiByteToWideChar(0, 0, PChar(ALinkName), len, wcLinkFileName, len) ] := #0; dwAttributes := Windows.getFileAttributesW(pwcLinkFileName); if dwAttributes <> FILE_DOES_NOT_EXIST then begin if not (optOverwrite in options) then begin if (dwAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then raise Exception.Create('The file "' + ALinkName + '" already exists'); if not isDirectoryEmpty(ALinkName) then raise Exception.Create( 'The directory "' + ALinkName + '" already exists and is not empty'); dwAttributes := FILE_DOES_NOT_EXIST; end else if ((dwAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0) then begin if not DeleteFile(ALinkName) then raise Exception.Create('Can''t overwrite file "' + ALinkName + '"'); dwAttributes := FILE_DOES_NOT_EXIST; end else if (dwAttributes and FILE_ATTRIBUTE_REPARSE_POINT) <> FILE_ATTRIBUTE_REPARSE_POINT then if not isDirectoryEmpty(ALinkName) then begin if not (optDirectory in options) then raise Exception.Create('Can''t overwrite non-empty directory "' + ALinkName + '"'); DeleteDirectoryContent(ALinkName); end; end; if dwAttributes = FILE_DOES_NOT_EXIST then begin Windows.CreateDirectoryW(pwcLinkFileName, nil); bDirectoryCreated := True; end; try hLink := Windows.CreateFileW(pwcLinkFileName, GENERIC_WRITE, 0, nil, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT or FILE_FLAG_BACKUP_SEMANTICS, 0); if hLink = INVALID_HANDLE_VALUE then RaiseLastOSError; len := Length(ATargetName); if ((PChar(ATargetName) + len - 1)^ = '\') and ((PChar(ATargetName) + len - 2)^ <> ':') then begin Dec(len); SetLength(ATargetName, len); end; len := Windows.GetFullPathName(PChar(ATargetName), MAX_PATH, aTargetFullName, pc); size := len + len + 2 + nNativeFileNamePrefixByteLength + REPARSE_MOUNTPOINT_HEADER_SIZE + 12; System.GetMem(pReparseInfo, size); FillChar(pReparseInfo^, size, #0); pwcTargetNativeFileName := @pReparseInfo^.ReparseTarget; System.Move(pwcNativeFileNamePrefix^, pwcTargetNativeFileName^, nNativeFileNamePrefixByteLength + 2); pwcTargetFileName := pwcTargetNativeFileName + nNativeFileNamePrefixWCharLength; pwc := pwcTargetFileName + Windows.MultiByteToWideChar(0, 0, aTargetFullName, len, pwcTargetFileName, len); pwc^ := #0; with pReparseInfo^ do begin ReparseTag := IO_REPARSE_TAG_MOUNT_POINT; ReparseTargetLength := PChar(pwc) - PChar(pwcTargetNativeFileName); ReparseTargetMaximumLength := ReparseTargetLength + 2; ReparseDataLength := ReparseTargetLength + 12; end; dwBytesReturned := 0; if not DeviceIoControl(hLink, FSCTL_SET_REPARSE_POINT, pReparseInfo, pReparseInfo^.ReparseDataLength + REPARSE_MOUNTPOINT_HEADER_SIZE, nil, 0, dwBytesReturned, nil) then RaiseLastOSError; except if bDirectoryCreated then RemoveDirectoryW(pwcLinkFileName); raise; end; Result := true; finally if hLink <> INVALID_HANDLE_VALUE then Windows.CloseHandle(hLink); if pwcLinkFileName <> nil then System.FreeMem(pwcLinkFileName); if pReparseInfo <> nil then System.FreeMem(pReparseInfo); end; end; //------------------------------------------------------------- procedure Help; begin WriteLn; WriteLn('Create link(s) on NTFS.'); WriteLn; WriteLn('Usage:'); WriteLn; WriteLn('To create hardlink(s) (works only for files):'); WriteLn('xlink [-fr] <existed_file> <link_name>'); WriteLn; WriteLn('To create symbolic link (works on Windows 2k/XP for directories only):'); WriteLn('xlink -s[f|F] <existed_directory> <link_name>'); WriteLn; WriteLn('Options:'); WriteLn('-f Overwrite file with name <link_name> if it exists.'); WriteLn('-F Overwrite file/directory with name <link_name> if it exists.'); WriteLn('-r Recursive.'); WriteLn; WriteLn('(c) 2002 Alex Konshin'); Halt; end; //------------------------------------------------------------- procedure Execute; var iArg: Integer; sArg: string; ptr: PChar; options: TOptions; sExistedFileName: string; sLink: string; dwAttrs: DWORD; begin iArg := 1; repeat sArg := ParamStr(iArg); if sArg = '' then Help; if PChar(sArg)^ <> '-' then Break; ptr := PChar(sArg) + 1; while ptr^ <> #0 do begin case ptr^ of 's', 'S': Include(options, optSymbolicLink); 'h', 'H': Help; 'F': options := options + [optOverwrite, optDirectory]; 'f': Include(options, optOverwrite); 'r', 'R': Include(options, optRecursive); 'd', 'D': Include(options, optDirectory); else WriteLn('Error: Invalid option ''-', ptr^, ''''); Exit; end; Inc(ptr); end; Inc(iArg); until iArg <= ParamCount; if ParamCount <= iArg then Help; if ParamCount - iArg > 1 then Include(options, optDirectory); if optSymbolicLink in options then begin sLink := ParamStr(ParamCount); repeat sExistedFileName := ParamStr(iArg); if not CreateSymlink(sExistedFileName, sLink, options) then WriteLn('The symbolic link creation failed.'); Inc(iArg); until iArg >= ParamCount; end else if (options * [optRecursive, optDirectory]) <> [] then begin sLink := ParamStr(ParamCount); repeat sExistedFileName := ParamStr(iArg); CreateHardlinksInDirectory(sExistedFileName, sLink, options); Inc(iArg); until iArg >= ParamCount; end else begin sExistedFileName := ParamStr(iArg); sLink := ParamStr(ParamCount); dwAttrs := GetFileAttributes(PChar(sExistedFileName)); if dwAttrs = FILE_DOES_NOT_EXIST then begin writeln('Error: The source file does not exist'); Exit; end; if (dwAttrs and FILE_ATTRIBUTE_DIRECTORY) <> 0 then begin writeln('Error: Can''t create hardlink for directory'); Exit; end; CreateHardlink(sExistedFileName, sLink, options); end; end; //============================================================= begin if ParamCount < 2 then Help; try Execute; except on E: Exception do begin WriteLn(E.ClassName + ': ' + E.Message); end; end; end. Программа на Delphi для создания жёстких ссылок и символических ссылок на файловых системах NTFS. Структура программы
Программа состоит из нескольких процедур:
1. Главная процедура
Главная процедура, Опции
Программа поддерживает несколько опций:
1. Обработка ошибок Программа использует исключения для обработки ошибок, таких как файл не найден, недопустимая опция или неудача создания ссылки. Ограничения NTFS Программа предназначена для файловых систем NTFS и имеет некоторые ограничения: 1. Жёсткие ссылки могут быть созданы только для файлов, а не для директорий. 2. Символические ссылки могут быть созданы только для директорий на Windows 2000/XP. 3. Программа не поддерживает создание жёстких ссылок или символических ссылок на сетевых ресурсах. Стиль кода Код написан в стиле, согласном с синтаксисом Pascal Delphi, и использует отступы и пробелы для улучшения читаемости. Однако некоторые части кода могут потребовать комментариев и дополнительной обработки ошибок. Создание hardlink и symbolic link на NTFS. В статье описывается утилита, которая позволяет создавать hardlinks и symbolic links на файловой системе NTFS. Hardlinks - это ссылки на файлы, которые не зависят от имени файла, а символические Комментарии и вопросыПолучайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.
|
||||
©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007 |