sysfile.inc 11 KB

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