sysfile.inc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2010 by Sven Barth
  4. Low leve file functions
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {*****************************************************************************
  12. Low Level File Routines
  13. *****************************************************************************}
  14. function do_isdevice(handle:thandle):boolean;
  15. begin
  16. do_isdevice := (handle = StdInputHandle) or
  17. (handle = StdOutputHandle) or
  18. (handle = StdErrorHandle);
  19. end;
  20. procedure do_close(h : thandle);
  21. begin
  22. if do_isdevice(h) then
  23. Exit;
  24. NtClose(h);
  25. end;
  26. procedure do_erase(p : pchar);
  27. var
  28. ntstr: TNtUnicodeString;
  29. objattr: TObjectAttributes;
  30. iostatus: TIOStatusBlock;
  31. h: THandle;
  32. disp: TFileDispositionInformation;
  33. res: LongInt;
  34. begin
  35. InoutRes := 4;
  36. DoDirSeparators(p);
  37. SysPCharToNtStr(ntstr, p, 0);
  38. SysInitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
  39. res := NtCreateFile(@h, NT_DELETE or NT_SYNCHRONIZE, @objattr, @iostatus, Nil,
  40. 0, FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
  41. FILE_OPEN, FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT,
  42. Nil, 0);
  43. if res >= 0 then begin
  44. disp.DeleteFile := True;
  45. res := NtSetInformationFile(h, @iostatus, @disp,
  46. SizeOf(TFileDispositionInformation), FileDispositionInformation);
  47. errno := res;
  48. NtClose(h);
  49. end else
  50. if res = STATUS_FILE_IS_A_DIRECTORY then
  51. errno := 2
  52. else
  53. errno := res;
  54. SysFreeNtStr(ntstr);
  55. Errno2InoutRes;
  56. end;
  57. procedure do_rename(p1,p2 : pchar);
  58. var
  59. h: THandle;
  60. objattr: TObjectAttributes;
  61. iostatus: TIOStatusBlock;
  62. dest, src: TNtUnicodeString;
  63. renameinfo: PFileRenameInformation;
  64. res: LongInt;
  65. begin
  66. DoDirSeparators(p1);
  67. DoDirSeparators(p2);
  68. { check whether the destination exists first }
  69. SysPCharToNtStr(dest, p2, 0);
  70. SysInitializeObjectAttributes(objattr, @dest, 0, 0, Nil);
  71. res := NtCreateFile(@h, 0, @objattr, @iostatus, Nil, 0,
  72. FILE_SHARE_READ or FILE_SHARE_WRITE, FILE_OPEN,
  73. FILE_NON_DIRECTORY_FILE, Nil, 0);
  74. if res >= 0 then begin
  75. { destination already exists => error }
  76. NtClose(h);
  77. errno := 5;
  78. Errno2InoutRes;
  79. end else begin
  80. SysPCharToNtStr(src, p1, 0);
  81. SysInitializeObjectAttributes(objattr, @src, 0, 0, Nil);
  82. res := NtCreateFile(@h, GENERIC_ALL or NT_SYNCHRONIZE or FILE_READ_ATTRIBUTES,
  83. @objattr, @iostatus, Nil, 0, FILE_SHARE_READ or FILE_SHARE_WRITE,
  84. FILE_OPEN, FILE_OPEN_FOR_BACKUP_INTENT or FILE_OPEN_REMOTE_INSTANCE
  85. or FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT, Nil,
  86. 0);
  87. if res >= 0 then begin
  88. renameinfo := GetMem(SizeOf(TFileRenameInformation) + dest.Length);
  89. with renameinfo^ do begin
  90. ReplaceIfExists := False;
  91. RootDirectory := 0;
  92. FileNameLength := dest.Length;
  93. Move(dest.Buffer^, renameinfo^.FileName, dest.Length);
  94. end;
  95. res := NtSetInformationFile(h, @iostatus, renameinfo,
  96. SizeOf(TFileRenameInformation) + dest.Length,
  97. FileRenameInformation);
  98. if res < 0 then begin
  99. { this could happen if src and destination reside on different drives,
  100. so we need to copy the file manually }
  101. {$message warning 'do_rename: Implement file copy!'}
  102. errno := res;
  103. Errno2InoutRes;
  104. end;
  105. NtClose(h);
  106. end else begin
  107. errno := res;
  108. Errno2InoutRes;
  109. end;
  110. SysFreeNtStr(src);
  111. end;
  112. SysFreeNtStr(dest);
  113. end;
  114. function do_write(h:thandle;addr:pointer;len : longint) : longint;
  115. var
  116. res: LongInt;
  117. iostatus: TIoStatusBlock;
  118. begin
  119. res := NtWriteFile(h, 0, Nil, Nil, @iostatus, addr, len, Nil, Nil);
  120. if res = STATUS_PENDING then begin
  121. res := NtWaitForSingleObject(h, False, Nil);
  122. if res >= 0 then
  123. res := iostatus.Status;
  124. end;
  125. if res < 0 then begin
  126. errno := res;
  127. Errno2InoutRes;
  128. do_write := 0;
  129. end else
  130. do_write := LongInt(iostatus.Information);
  131. end;
  132. function do_read(h: thandle; addr: pointer; len: longint): longint;
  133. var
  134. iostatus: TIOStatusBlock;
  135. res: LongInt;
  136. begin
  137. res := NtReadFile(h, 0, Nil, Nil, @iostatus, addr, len, Nil, Nil);
  138. if res = STATUS_PENDING then begin
  139. res := NtWaitForSingleObject(h, False, Nil);
  140. if res >= 0 then
  141. res := iostatus.Status;
  142. end;
  143. if (res < 0) and (res <> STATUS_PIPE_BROKEN) then begin
  144. errno := res;
  145. Errno2InoutRes;
  146. do_read := 0;
  147. end else
  148. if res = STATUS_PIPE_BROKEN then
  149. do_read := 0
  150. else
  151. do_read := LongInt(iostatus.Information);
  152. end;
  153. function do_filepos(handle : thandle) : Int64;
  154. var
  155. res: LongInt;
  156. iostatus: TIoStatusBlock;
  157. position: TFilePositionInformation;
  158. begin
  159. res := NtQueryInformationFile(handle, @iostatus, @position,
  160. SizeOf(TFilePositionInformation), FilePositionInformation);
  161. if res < 0 then begin
  162. errno := res;
  163. Errno2InoutRes;
  164. do_filepos := 0;
  165. end else
  166. do_filepos := position.CurrentByteOffset.QuadPart;
  167. end;
  168. procedure do_seek(handle: thandle; pos: Int64);
  169. var
  170. position: TFilePositionInformation;
  171. iostatus: TIoStatusBlock;
  172. res: LongInt;
  173. begin
  174. position.CurrentByteOffset.QuadPart := pos;
  175. res := NtSetInformationFile(handle, @iostatus, @position,
  176. SizeOf(TFilePositionInformation), FilePositionInformation);
  177. if res < 0 then begin
  178. errno := res;
  179. Errno2InoutRes;
  180. end;
  181. end;
  182. function do_seekend(handle:thandle):Int64;
  183. var
  184. res: LongInt;
  185. standard: TFileStandardInformation;
  186. position: TFilePositionInformation;
  187. iostatus: TIoStatusBlock;
  188. begin
  189. do_seekend := 0;
  190. res := NtQueryInformationFile(handle, @iostatus, @standard,
  191. SizeOf(TFileStandardInformation), FileStandardInformation);
  192. if res >= 0 then begin
  193. position.CurrentByteOffset.QuadPart := standard.EndOfFile.QuadPart;
  194. res := NtSetInformationFile(handle, @iostatus, @position,
  195. SizeOf(TFilePositionInformation), FilePositionInformation);
  196. if res >= 0 then
  197. do_seekend := position.CurrentByteOffset.QuadPart;
  198. end;
  199. if res < 0 then begin
  200. errno := res;
  201. Errno2InoutRes;
  202. end;
  203. end;
  204. function do_filesize(handle : thandle) : Int64;
  205. var
  206. res: LongInt;
  207. iostatus: TIoStatusBlock;
  208. standard: TFileStandardInformation;
  209. begin
  210. res := NtQueryInformationFile(handle, @iostatus, @standard,
  211. SizeOf(TFileStandardInformation), FileStandardInformation);
  212. if res >= 0 then
  213. do_filesize := standard.EndOfFile.QuadPart
  214. else begin
  215. errno := res;
  216. Errno2InoutRes;
  217. do_filesize := 0;
  218. end;
  219. end;
  220. procedure do_truncate (handle:thandle;pos:Int64);
  221. var
  222. endoffileinfo: TFileEndOfFileInformation;
  223. allocinfo: TFileAllocationInformation;
  224. iostatus: TIoStatusBlock;
  225. res: LongInt;
  226. begin
  227. // based on ReactOS' SetEndOfFile
  228. endoffileinfo.EndOfFile.QuadPart := pos;
  229. res := NtSetInformationFile(handle, @iostatus, @endoffileinfo,
  230. SizeOf(TFileEndOfFileInformation), FileEndOfFileInformation);
  231. if res < 0 then begin
  232. errno := res;
  233. Errno2InoutRes;
  234. end else begin
  235. allocinfo.AllocationSize.QuadPart := pos;
  236. res := NtSetInformationFile(handle, @iostatus, @allocinfo,
  237. SizeOf(TFileAllocationInformation), FileAllocationInformation);
  238. if res < 0 then begin
  239. errno := res;
  240. Errno2InoutRes;
  241. end;
  242. end;
  243. end;
  244. procedure do_open(var f;p:pchar;flags:longint);
  245. {
  246. filerec and textrec have both handle and mode as the first items so
  247. they could use the same routine for opening/creating.
  248. when (flags and $100) the file will be append
  249. when (flags and $1000) the file will be truncate/rewritten
  250. when (flags and $10000) there is no check for close (needed for textfiles)
  251. }
  252. var
  253. shflags, cd, oflags: LongWord;
  254. objattr: TObjectAttributes;
  255. iostatus: TIoStatusBlock;
  256. ntstr: TNtUnicodeString;
  257. res: LongInt;
  258. begin
  259. DoDirSeparators(p);
  260. { close first if opened }
  261. if ((flags and $10000)=0) then
  262. begin
  263. case filerec(f).mode of
  264. fminput,fmoutput,fminout : Do_Close(filerec(f).handle);
  265. fmclosed : ;
  266. else
  267. begin
  268. {not assigned}
  269. inoutres:=102;
  270. exit;
  271. end;
  272. end;
  273. end;
  274. { reset file handle }
  275. filerec(f).handle:=UnusedHandle;
  276. { convert filesharing }
  277. shflags := 0;
  278. if ((filemode and fmshareExclusive) = fmshareExclusive) then
  279. { no sharing }
  280. else
  281. if (filemode = fmShareCompat) or ((filemode and fmshareDenyWrite) = fmshareDenyWrite) then
  282. shflags := FILE_SHARE_READ
  283. else
  284. if ((filemode and fmshareDenyRead) = fmshareDenyRead) then
  285. shflags := FILE_SHARE_WRITE
  286. else
  287. if ((filemode and fmshareDenyNone) = fmshareDenyNone) then
  288. shflags := FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE;
  289. { convert filemode to filerec modes }
  290. case (flags and 3) of
  291. 0 : begin
  292. filerec(f).mode:=fminput;
  293. oflags := GENERIC_READ;
  294. end;
  295. 1 : begin
  296. filerec(f).mode:=fmoutput;
  297. oflags := GENERIC_WRITE;
  298. end;
  299. 2 : begin
  300. filerec(f).mode:=fminout;
  301. oflags := GENERIC_WRITE or GENERIC_READ;
  302. end;
  303. end;
  304. oflags := oflags or NT_SYNCHRONIZE or FILE_READ_ATTRIBUTES;
  305. { create it ? }
  306. if (flags and $1000) <> 0 then
  307. cd := FILE_OVERWRITE_IF
  308. { or Append/Open ? }
  309. else
  310. cd := FILE_OPEN;
  311. { empty name is special }
  312. { console i/o not supported yet }
  313. if p[0]=#0 then
  314. begin
  315. case FileRec(f).mode of
  316. fminput :
  317. FileRec(f).Handle:=StdInputHandle;
  318. fminout, { this is set by rewrite }
  319. fmoutput :
  320. FileRec(f).Handle:=StdOutputHandle;
  321. fmappend :
  322. begin
  323. FileRec(f).Handle:=StdOutputHandle;
  324. FileRec(f).mode:=fmoutput; {fool fmappend}
  325. end;
  326. end;
  327. exit;
  328. end;
  329. SysPCharToNtStr(ntstr, p, 0);
  330. SysInitializeObjectAttributes(objattr, @ntstr, OBJ_INHERIT, 0, Nil);
  331. res := NtCreateFile(@filerec(f).handle, oflags, @objattr, @iostatus, Nil,
  332. FILE_ATTRIBUTE_NORMAL, shflags, cd,
  333. FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT, Nil, 0);
  334. SysFreeNtStr(ntstr);
  335. { append mode }
  336. if (flags and $100 <> 0) and (res >= 0) then begin
  337. do_seekend(filerec(f).handle);
  338. filerec(f).mode := fmoutput; {fool fmappend}
  339. end;
  340. { get errors }
  341. if res < 0 then begin
  342. errno := res;
  343. Errno2InoutRes;
  344. end;
  345. end;