sysfile.inc 11 KB

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