|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 /** |
|
6 * Synchronous front-end for the JavaScript OS.File library. |
|
7 * Windows implementation. |
|
8 * |
|
9 * This front-end is meant to be imported by a worker thread. |
|
10 */ |
|
11 |
|
12 { |
|
13 if (typeof Components != "undefined") { |
|
14 // We do not wish osfile_win_front.jsm to be used directly as a main thread |
|
15 // module yet. |
|
16 throw new Error("osfile_win_front.jsm cannot be used from the main thread yet"); |
|
17 } |
|
18 |
|
19 (function(exports) { |
|
20 "use strict"; |
|
21 |
|
22 |
|
23 // exports.OS.Win is created by osfile_win_back.jsm |
|
24 if (exports.OS && exports.OS.File) { |
|
25 return; // Avoid double-initialization |
|
26 } |
|
27 |
|
28 let SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm"); |
|
29 let Path = require("resource://gre/modules/osfile/ospath.jsm"); |
|
30 let SysAll = require("resource://gre/modules/osfile/osfile_win_allthreads.jsm"); |
|
31 exports.OS.Win.File._init(); |
|
32 let Const = exports.OS.Constants.Win; |
|
33 let WinFile = exports.OS.Win.File; |
|
34 let Type = WinFile.Type; |
|
35 |
|
36 // Mutable thread-global data |
|
37 // In the Windows implementation, methods |read| and |write| |
|
38 // require passing a pointer to an uint32 to determine how many |
|
39 // bytes have been read/written. In C, this is a benigne operation, |
|
40 // but in js-ctypes, this has a cost. Rather than re-allocating a |
|
41 // C uint32 and a C uint32* for each |read|/|write|, we take advantage |
|
42 // of the fact that the state is thread-private -- hence that two |
|
43 // |read|/|write| operations cannot take place at the same time -- |
|
44 // and we use the following global mutable values: |
|
45 let gBytesRead = new ctypes.uint32_t(0); |
|
46 let gBytesReadPtr = gBytesRead.address(); |
|
47 let gBytesWritten = new ctypes.uint32_t(0); |
|
48 let gBytesWrittenPtr = gBytesWritten.address(); |
|
49 |
|
50 // Same story for GetFileInformationByHandle |
|
51 let gFileInfo = new Type.FILE_INFORMATION.implementation(); |
|
52 let gFileInfoPtr = gFileInfo.address(); |
|
53 |
|
54 /** |
|
55 * Representation of a file. |
|
56 * |
|
57 * You generally do not need to call this constructor yourself. Rather, |
|
58 * to open a file, use function |OS.File.open|. |
|
59 * |
|
60 * @param fd A OS-specific file descriptor. |
|
61 * @param {string} path File path of the file handle, used for error-reporting. |
|
62 * @constructor |
|
63 */ |
|
64 let File = function File(fd, path) { |
|
65 exports.OS.Shared.AbstractFile.call(this, fd, path); |
|
66 this._closeResult = null; |
|
67 }; |
|
68 File.prototype = Object.create(exports.OS.Shared.AbstractFile.prototype); |
|
69 |
|
70 /** |
|
71 * Close the file. |
|
72 * |
|
73 * This method has no effect if the file is already closed. However, |
|
74 * if the first call to |close| has thrown an error, further calls |
|
75 * will throw the same error. |
|
76 * |
|
77 * @throws File.Error If closing the file revealed an error that could |
|
78 * not be reported earlier. |
|
79 */ |
|
80 File.prototype.close = function close() { |
|
81 if (this._fd) { |
|
82 let fd = this._fd; |
|
83 this._fd = null; |
|
84 // Call |close(fd)|, detach finalizer if any |
|
85 // (|fd| may not be a CDataFinalizer if it has been |
|
86 // instantiated from a controller thread). |
|
87 let result = WinFile._CloseHandle(fd); |
|
88 if (typeof fd == "object" && "forget" in fd) { |
|
89 fd.forget(); |
|
90 } |
|
91 if (result == -1) { |
|
92 this._closeResult = new File.Error("close", ctypes.winLastError, this._path); |
|
93 } |
|
94 } |
|
95 if (this._closeResult) { |
|
96 throw this._closeResult; |
|
97 } |
|
98 return; |
|
99 }; |
|
100 |
|
101 /** |
|
102 * Read some bytes from a file. |
|
103 * |
|
104 * @param {C pointer} buffer A buffer for holding the data |
|
105 * once it is read. |
|
106 * @param {number} nbytes The number of bytes to read. It must not |
|
107 * exceed the size of |buffer| in bytes but it may exceed the number |
|
108 * of bytes unread in the file. |
|
109 * @param {*=} options Additional options for reading. Ignored in |
|
110 * this implementation. |
|
111 * |
|
112 * @return {number} The number of bytes effectively read. If zero, |
|
113 * the end of the file has been reached. |
|
114 * @throws {OS.File.Error} In case of I/O error. |
|
115 */ |
|
116 File.prototype._read = function _read(buffer, nbytes, options) { |
|
117 // |gBytesReadPtr| is a pointer to |gBytesRead|. |
|
118 throw_on_zero("read", |
|
119 WinFile.ReadFile(this.fd, buffer, nbytes, gBytesReadPtr, null), |
|
120 this._path |
|
121 ); |
|
122 return gBytesRead.value; |
|
123 }; |
|
124 |
|
125 /** |
|
126 * Write some bytes to a file. |
|
127 * |
|
128 * @param {C pointer} buffer A buffer holding the data that must be |
|
129 * written. |
|
130 * @param {number} nbytes The number of bytes to write. It must not |
|
131 * exceed the size of |buffer| in bytes. |
|
132 * @param {*=} options Additional options for writing. Ignored in |
|
133 * this implementation. |
|
134 * |
|
135 * @return {number} The number of bytes effectively written. |
|
136 * @throws {OS.File.Error} In case of I/O error. |
|
137 */ |
|
138 File.prototype._write = function _write(buffer, nbytes, options) { |
|
139 if (this._appendMode) { |
|
140 // Need to manually seek on Windows, as O_APPEND is not supported. |
|
141 // This is, of course, a race, but there is no real way around this. |
|
142 this.setPosition(0, File.POS_END); |
|
143 } |
|
144 // |gBytesWrittenPtr| is a pointer to |gBytesWritten|. |
|
145 throw_on_zero("write", |
|
146 WinFile.WriteFile(this.fd, buffer, nbytes, gBytesWrittenPtr, null), |
|
147 this._path |
|
148 ); |
|
149 return gBytesWritten.value; |
|
150 }; |
|
151 |
|
152 /** |
|
153 * Return the current position in the file. |
|
154 */ |
|
155 File.prototype.getPosition = function getPosition(pos) { |
|
156 return this.setPosition(0, File.POS_CURRENT); |
|
157 }; |
|
158 |
|
159 /** |
|
160 * Change the current position in the file. |
|
161 * |
|
162 * @param {number} pos The new position. Whether this position |
|
163 * is considered from the current position, from the start of |
|
164 * the file or from the end of the file is determined by |
|
165 * argument |whence|. Note that |pos| may exceed the length of |
|
166 * the file. |
|
167 * @param {number=} whence The reference position. If omitted |
|
168 * or |OS.File.POS_START|, |pos| is relative to the start of the |
|
169 * file. If |OS.File.POS_CURRENT|, |pos| is relative to the |
|
170 * current position in the file. If |OS.File.POS_END|, |pos| is |
|
171 * relative to the end of the file. |
|
172 * |
|
173 * @return The new position in the file. |
|
174 */ |
|
175 File.prototype.setPosition = function setPosition(pos, whence) { |
|
176 if (whence === undefined) { |
|
177 whence = Const.FILE_BEGIN; |
|
178 } |
|
179 let pos64 = ctypes.Int64(pos); |
|
180 // Per MSDN, while |lDistanceToMove| (low) is declared as int32_t, when |
|
181 // providing |lDistanceToMoveHigh| as well, it should countain the |
|
182 // bottom 32 bits of the 64-bit integer. Hence the following |posLo| |
|
183 // cast is OK. |
|
184 let posLo = new ctypes.uint32_t(ctypes.Int64.lo(pos64)); |
|
185 posLo = ctypes.cast(posLo, ctypes.int32_t); |
|
186 let posHi = new ctypes.int32_t(ctypes.Int64.hi(pos64)); |
|
187 let result = WinFile.SetFilePointer( |
|
188 this.fd, posLo.value, posHi.address(), whence); |
|
189 // INVALID_SET_FILE_POINTER might be still a valid result, as it |
|
190 // represents the lower 32 bit of the int64 result. MSDN says to check |
|
191 // both, INVALID_SET_FILE_POINTER and a non-zero winLastError. |
|
192 if (result == Const.INVALID_SET_FILE_POINTER && ctypes.winLastError) { |
|
193 throw new File.Error("setPosition", ctypes.winLastError, this._path); |
|
194 } |
|
195 pos64 = ctypes.Int64.join(posHi.value, result); |
|
196 return Type.int64_t.project(pos64); |
|
197 }; |
|
198 |
|
199 /** |
|
200 * Fetch the information on the file. |
|
201 * |
|
202 * @return File.Info The information on |this| file. |
|
203 */ |
|
204 File.prototype.stat = function stat() { |
|
205 throw_on_zero("stat", |
|
206 WinFile.GetFileInformationByHandle(this.fd, gFileInfoPtr), |
|
207 this._path); |
|
208 return new File.Info(gFileInfo, this._path); |
|
209 }; |
|
210 |
|
211 /** |
|
212 * Set the last access and modification date of the file. |
|
213 * The time stamp resolution is 1 second at best, but might be worse |
|
214 * depending on the platform. |
|
215 * |
|
216 * @param {Date,number=} accessDate The last access date. If numeric, |
|
217 * milliseconds since epoch. If omitted or null, then the current date |
|
218 * will be used. |
|
219 * @param {Date,number=} modificationDate The last modification date. If |
|
220 * numeric, milliseconds since epoch. If omitted or null, then the current |
|
221 * date will be used. |
|
222 * |
|
223 * @throws {TypeError} In case of invalid parameters. |
|
224 * @throws {OS.File.Error} In case of I/O error. |
|
225 */ |
|
226 File.prototype.setDates = function setDates(accessDate, modificationDate) { |
|
227 accessDate = Date_to_FILETIME("File.prototype.setDates", accessDate, this._path); |
|
228 modificationDate = Date_to_FILETIME("File.prototype.setDates", |
|
229 modificationDate, |
|
230 this._path); |
|
231 throw_on_zero("setDates", |
|
232 WinFile.SetFileTime(this.fd, null, accessDate.address(), |
|
233 modificationDate.address()), |
|
234 this._path); |
|
235 }; |
|
236 |
|
237 /** |
|
238 * Set the file's access permission bits. |
|
239 * Not implemented for Windows (bug 1022816). |
|
240 */ |
|
241 File.prototype.setPermissions = function setPermissions(options = {}) { |
|
242 // do nothing |
|
243 }; |
|
244 |
|
245 /** |
|
246 * Flushes the file's buffers and causes all buffered data |
|
247 * to be written. |
|
248 * Disk flushes are very expensive and therefore should be used carefully, |
|
249 * sparingly and only in scenarios where it is vital that data survives |
|
250 * system crashes. Even though the function will be executed off the |
|
251 * main-thread, it might still affect the overall performance of any |
|
252 * running application. |
|
253 * |
|
254 * @throws {OS.File.Error} In case of I/O error. |
|
255 */ |
|
256 File.prototype.flush = function flush() { |
|
257 throw_on_zero("flush", WinFile.FlushFileBuffers(this.fd), this._path); |
|
258 }; |
|
259 |
|
260 // The default sharing mode for opening files: files are not |
|
261 // locked against being reopened for reading/writing or against |
|
262 // being deleted by the same process or another process. |
|
263 // This is consistent with the default Unix policy. |
|
264 const DEFAULT_SHARE = Const.FILE_SHARE_READ | |
|
265 Const.FILE_SHARE_WRITE | Const.FILE_SHARE_DELETE; |
|
266 |
|
267 // The default flags for opening files. |
|
268 const DEFAULT_FLAGS = Const.FILE_ATTRIBUTE_NORMAL; |
|
269 |
|
270 /** |
|
271 * Open a file |
|
272 * |
|
273 * @param {string} path The path to the file. |
|
274 * @param {*=} mode The opening mode for the file, as |
|
275 * an object that may contain the following fields: |
|
276 * |
|
277 * - {bool} truncate If |true|, the file will be opened |
|
278 * for writing. If the file does not exist, it will be |
|
279 * created. If the file exists, its contents will be |
|
280 * erased. Cannot be specified with |create|. |
|
281 * - {bool} create If |true|, the file will be opened |
|
282 * for writing. If the file exists, this function fails. |
|
283 * If the file does not exist, it will be created. Cannot |
|
284 * be specified with |truncate| or |existing|. |
|
285 * - {bool} existing. If the file does not exist, this function |
|
286 * fails. Cannot be specified with |create|. |
|
287 * - {bool} read If |true|, the file will be opened for |
|
288 * reading. The file may also be opened for writing, depending |
|
289 * on the other fields of |mode|. |
|
290 * - {bool} write If |true|, the file will be opened for |
|
291 * writing. The file may also be opened for reading, depending |
|
292 * on the other fields of |mode|. |
|
293 * - {bool} append If |true|, the file will be opened for appending, |
|
294 * meaning the equivalent of |.setPosition(0, POS_END)| is executed |
|
295 * before each write. The default is |true|, i.e. opening a file for |
|
296 * appending. Specify |append: false| to open the file in regular mode. |
|
297 * |
|
298 * If neither |truncate|, |create| or |write| is specified, the file |
|
299 * is opened for reading. |
|
300 * |
|
301 * Note that |false|, |null| or |undefined| flags are simply ignored. |
|
302 * |
|
303 * @param {*=} options Additional options for file opening. This |
|
304 * implementation interprets the following fields: |
|
305 * |
|
306 * - {number} winShare If specified, a share mode, as per |
|
307 * Windows function |CreateFile|. You can build it from |
|
308 * constants |OS.Constants.Win.FILE_SHARE_*|. If unspecified, |
|
309 * the file uses the default sharing policy: it can be opened |
|
310 * for reading and/or writing and it can be removed by other |
|
311 * processes and by the same process. |
|
312 * - {number} winSecurity If specified, Windows security |
|
313 * attributes, as per Windows function |CreateFile|. If unspecified, |
|
314 * no security attributes. |
|
315 * - {number} winAccess If specified, Windows access mode, as |
|
316 * per Windows function |CreateFile|. This also requires option |
|
317 * |winDisposition| and this replaces argument |mode|. If unspecified, |
|
318 * uses the string |mode|. |
|
319 * - {number} winDisposition If specified, Windows disposition mode, |
|
320 * as per Windows function |CreateFile|. This also requires option |
|
321 * |winAccess| and this replaces argument |mode|. If unspecified, |
|
322 * uses the string |mode|. |
|
323 * |
|
324 * @return {File} A file object. |
|
325 * @throws {OS.File.Error} If the file could not be opened. |
|
326 */ |
|
327 File.open = function Win_open(path, mode = {}, options = {}) { |
|
328 let share = options.winShare !== undefined ? options.winShare : DEFAULT_SHARE; |
|
329 let security = options.winSecurity || null; |
|
330 let flags = options.winFlags !== undefined ? options.winFlags : DEFAULT_FLAGS; |
|
331 let template = options.winTemplate ? options.winTemplate._fd : null; |
|
332 let access; |
|
333 let disposition; |
|
334 |
|
335 mode = OS.Shared.AbstractFile.normalizeOpenMode(mode); |
|
336 |
|
337 if ("winAccess" in options && "winDisposition" in options) { |
|
338 access = options.winAccess; |
|
339 disposition = options.winDisposition; |
|
340 } else if (("winAccess" in options && !("winDisposition" in options)) |
|
341 ||(!("winAccess" in options) && "winDisposition" in options)) { |
|
342 throw new TypeError("OS.File.open requires either both options " + |
|
343 "winAccess and winDisposition or neither"); |
|
344 } else { |
|
345 if (mode.read) { |
|
346 access |= Const.GENERIC_READ; |
|
347 } |
|
348 if (mode.write) { |
|
349 access |= Const.GENERIC_WRITE; |
|
350 } |
|
351 // Finally, handle create/existing/trunc |
|
352 if (mode.trunc) { |
|
353 if (mode.existing) { |
|
354 // It seems that Const.TRUNCATE_EXISTING is broken |
|
355 // in presence of links (source, anyone?). We need |
|
356 // to open normally, then perform truncation manually. |
|
357 disposition = Const.OPEN_EXISTING; |
|
358 } else { |
|
359 disposition = Const.CREATE_ALWAYS; |
|
360 } |
|
361 } else if (mode.create) { |
|
362 disposition = Const.CREATE_NEW; |
|
363 } else if (mode.read && !mode.write) { |
|
364 disposition = Const.OPEN_EXISTING; |
|
365 } else if (mode.existing) { |
|
366 disposition = Const.OPEN_EXISTING; |
|
367 } else { |
|
368 disposition = Const.OPEN_ALWAYS; |
|
369 } |
|
370 } |
|
371 |
|
372 let file = error_or_file(WinFile.CreateFile(path, |
|
373 access, share, security, disposition, flags, template), path); |
|
374 |
|
375 file._appendMode = !!mode.append; |
|
376 |
|
377 if (!(mode.trunc && mode.existing)) { |
|
378 return file; |
|
379 } |
|
380 // Now, perform manual truncation |
|
381 file.setPosition(0, File.POS_START); |
|
382 throw_on_zero("open", |
|
383 WinFile.SetEndOfFile(file.fd), |
|
384 path); |
|
385 return file; |
|
386 }; |
|
387 |
|
388 /** |
|
389 * Checks if a file or directory exists |
|
390 * |
|
391 * @param {string} path The path to the file. |
|
392 * |
|
393 * @return {bool} true if the file exists, false otherwise. |
|
394 */ |
|
395 File.exists = function Win_exists(path) { |
|
396 try { |
|
397 let file = File.open(path, FILE_STAT_MODE, FILE_STAT_OPTIONS); |
|
398 file.close(); |
|
399 return true; |
|
400 } catch (x) { |
|
401 return false; |
|
402 } |
|
403 }; |
|
404 |
|
405 /** |
|
406 * Remove an existing file. |
|
407 * |
|
408 * @param {string} path The name of the file. |
|
409 * @param {*=} options Additional options. |
|
410 * - {bool} ignoreAbsent If |false|, throw an error if the file does |
|
411 * not exist. |true| by default. |
|
412 * |
|
413 * @throws {OS.File.Error} In case of I/O error. |
|
414 */ |
|
415 File.remove = function remove(path, options = {}) { |
|
416 if (WinFile.DeleteFile(path)) { |
|
417 return; |
|
418 } |
|
419 |
|
420 if (ctypes.winLastError == Const.ERROR_FILE_NOT_FOUND) { |
|
421 if ((!("ignoreAbsent" in options) || options.ignoreAbsent)) { |
|
422 return; |
|
423 } |
|
424 } else if (ctypes.winLastError == Const.ERROR_ACCESS_DENIED) { |
|
425 let attributes = WinFile.GetFileAttributes(path); |
|
426 if (attributes != Const.INVALID_FILE_ATTRIBUTES && |
|
427 attributes & Const.FILE_ATTRIBUTE_READONLY) { |
|
428 let newAttributes = attributes & ~Const.FILE_ATTRIBUTE_READONLY; |
|
429 if (WinFile.SetFileAttributes(path, newAttributes) && |
|
430 WinFile.DeleteFile(path)) { |
|
431 return; |
|
432 } |
|
433 } |
|
434 } |
|
435 |
|
436 throw new File.Error("remove", ctypes.winLastError, path); |
|
437 }; |
|
438 |
|
439 /** |
|
440 * Remove an empty directory. |
|
441 * |
|
442 * @param {string} path The name of the directory to remove. |
|
443 * @param {*=} options Additional options. |
|
444 * - {bool} ignoreAbsent If |false|, throw an error if the directory |
|
445 * does not exist. |true| by default |
|
446 */ |
|
447 File.removeEmptyDir = function removeEmptyDir(path, options = {}) { |
|
448 let result = WinFile.RemoveDirectory(path); |
|
449 if (!result) { |
|
450 if ((!("ignoreAbsent" in options) || options.ignoreAbsent) && |
|
451 ctypes.winLastError == Const.ERROR_FILE_NOT_FOUND) { |
|
452 return; |
|
453 } |
|
454 throw new File.Error("removeEmptyDir", ctypes.winLastError, path); |
|
455 } |
|
456 }; |
|
457 |
|
458 /** |
|
459 * Create a directory and, optionally, its parent directories. |
|
460 * |
|
461 * @param {string} path The name of the directory. |
|
462 * @param {*=} options Additional options. This |
|
463 * implementation interprets the following fields: |
|
464 * |
|
465 * - {C pointer} winSecurity If specified, security attributes |
|
466 * as per winapi function |CreateDirectory|. If unspecified, |
|
467 * use the default security descriptor, inherited from the |
|
468 * parent directory. |
|
469 * - {bool} ignoreExisting If |false|, throw an error if the directory |
|
470 * already exists. |true| by default |
|
471 * - {string} from If specified, the call to |makeDir| creates all the |
|
472 * ancestors of |path| that are descendants of |from|. Note that |from| |
|
473 * and its existing descendants must be user-writeable and that |path| |
|
474 * must be a descendant of |from|. |
|
475 * Example: |
|
476 * makeDir(Path.join(profileDir, "foo", "bar"), { from: profileDir }); |
|
477 * creates directories profileDir/foo, profileDir/foo/bar |
|
478 */ |
|
479 File._makeDir = function makeDir(path, options = {}) { |
|
480 let security = options.winSecurity || null; |
|
481 let result = WinFile.CreateDirectory(path, security); |
|
482 |
|
483 if (result) { |
|
484 return; |
|
485 } |
|
486 |
|
487 if (("ignoreExisting" in options) && !options.ignoreExisting) { |
|
488 throw new File.Error("makeDir", ctypes.winLastError, path); |
|
489 } |
|
490 |
|
491 if (ctypes.winLastError == Const.ERROR_ALREADY_EXISTS) { |
|
492 return; |
|
493 } |
|
494 |
|
495 // If the user has no access, but it's a root directory, no error should be thrown |
|
496 let splitPath = OS.Path.split(path); |
|
497 // Removing last component if it's empty |
|
498 // An empty last component is caused by trailing slashes in path |
|
499 // This is always the case with root directories |
|
500 if( splitPath.components[splitPath.components.length - 1].length === 0 ) { |
|
501 splitPath.components.pop(); |
|
502 } |
|
503 // One component consisting of a drive letter implies a directory root. |
|
504 if (ctypes.winLastError == Const.ERROR_ACCESS_DENIED && |
|
505 splitPath.winDrive && |
|
506 splitPath.components.length === 1 ) { |
|
507 return; |
|
508 } |
|
509 |
|
510 throw new File.Error("makeDir", ctypes.winLastError, path); |
|
511 }; |
|
512 |
|
513 /** |
|
514 * Copy a file to a destination. |
|
515 * |
|
516 * @param {string} sourcePath The platform-specific path at which |
|
517 * the file may currently be found. |
|
518 * @param {string} destPath The platform-specific path at which the |
|
519 * file should be copied. |
|
520 * @param {*=} options An object which may contain the following fields: |
|
521 * |
|
522 * @option {bool} noOverwrite - If true, this function will fail if |
|
523 * a file already exists at |destPath|. Otherwise, if this file exists, |
|
524 * it will be erased silently. |
|
525 * |
|
526 * @throws {OS.File.Error} In case of any error. |
|
527 * |
|
528 * General note: The behavior of this function is defined only when |
|
529 * it is called on a single file. If it is called on a directory, the |
|
530 * behavior is undefined and may not be the same across all platforms. |
|
531 * |
|
532 * General note: The behavior of this function with respect to metadata |
|
533 * is unspecified. Metadata may or may not be copied with the file. The |
|
534 * behavior may not be the same across all platforms. |
|
535 */ |
|
536 File.copy = function copy(sourcePath, destPath, options = {}) { |
|
537 throw_on_zero("copy", |
|
538 WinFile.CopyFile(sourcePath, destPath, options.noOverwrite || false), |
|
539 sourcePath |
|
540 ); |
|
541 }; |
|
542 |
|
543 /** |
|
544 * Move a file to a destination. |
|
545 * |
|
546 * @param {string} sourcePath The platform-specific path at which |
|
547 * the file may currently be found. |
|
548 * @param {string} destPath The platform-specific path at which the |
|
549 * file should be moved. |
|
550 * @param {*=} options An object which may contain the following fields: |
|
551 * |
|
552 * @option {bool} noOverwrite - If set, this function will fail if |
|
553 * a file already exists at |destPath|. Otherwise, if this file exists, |
|
554 * it will be erased silently. |
|
555 * @option {bool} noCopy - If set, this function will fail if the |
|
556 * operation is more sophisticated than a simple renaming, i.e. if |
|
557 * |sourcePath| and |destPath| are not situated on the same drive. |
|
558 * |
|
559 * @throws {OS.File.Error} In case of any error. |
|
560 * |
|
561 * General note: The behavior of this function is defined only when |
|
562 * it is called on a single file. If it is called on a directory, the |
|
563 * behavior is undefined and may not be the same across all platforms. |
|
564 * |
|
565 * General note: The behavior of this function with respect to metadata |
|
566 * is unspecified. Metadata may or may not be moved with the file. The |
|
567 * behavior may not be the same across all platforms. |
|
568 */ |
|
569 File.move = function move(sourcePath, destPath, options = {}) { |
|
570 let flags = 0; |
|
571 if (!options.noCopy) { |
|
572 flags = Const.MOVEFILE_COPY_ALLOWED; |
|
573 } |
|
574 if (!options.noOverwrite) { |
|
575 flags = flags | Const.MOVEFILE_REPLACE_EXISTING; |
|
576 } |
|
577 throw_on_zero("move", |
|
578 WinFile.MoveFileEx(sourcePath, destPath, flags), |
|
579 sourcePath |
|
580 ); |
|
581 |
|
582 // Inherit NTFS permissions from the destination directory |
|
583 // if possible. |
|
584 if (Path.dirname(sourcePath) === Path.dirname(destPath)) { |
|
585 // Skip if the move operation was the simple rename, |
|
586 return; |
|
587 } |
|
588 // The function may fail for various reasons (e.g. not all |
|
589 // filesystems support NTFS permissions or the user may not |
|
590 // have the enough rights to read/write permissions). |
|
591 // However we can safely ignore errors. The file was already |
|
592 // moved. Setting permissions is not mandatory. |
|
593 let dacl = new ctypes.voidptr_t(); |
|
594 let sd = new ctypes.voidptr_t(); |
|
595 WinFile.GetNamedSecurityInfo(destPath, Const.SE_FILE_OBJECT, |
|
596 Const.DACL_SECURITY_INFORMATION, |
|
597 null /*sidOwner*/, null /*sidGroup*/, |
|
598 dacl.address(), null /*sacl*/, |
|
599 sd.address()); |
|
600 // dacl will be set only if the function succeeds. |
|
601 if (!dacl.isNull()) { |
|
602 WinFile.SetNamedSecurityInfo(destPath, Const.SE_FILE_OBJECT, |
|
603 Const.DACL_SECURITY_INFORMATION | |
|
604 Const.UNPROTECTED_DACL_SECURITY_INFORMATION, |
|
605 null /*sidOwner*/, null /*sidGroup*/, |
|
606 dacl, null /*sacl*/); |
|
607 } |
|
608 // sd will be set only if the function succeeds. |
|
609 if (!sd.isNull()) { |
|
610 WinFile.LocalFree(Type.HLOCAL.cast(sd)); |
|
611 } |
|
612 }; |
|
613 |
|
614 /** |
|
615 * Gets the number of bytes available on disk to the current user. |
|
616 * |
|
617 * @param {string} sourcePath Platform-specific path to a directory on |
|
618 * the disk to query for free available bytes. |
|
619 * |
|
620 * @return {number} The number of bytes available for the current user. |
|
621 * @throws {OS.File.Error} In case of any error. |
|
622 */ |
|
623 File.getAvailableFreeSpace = function Win_getAvailableFreeSpace(sourcePath) { |
|
624 let freeBytesAvailableToUser = new Type.uint64_t.implementation(0); |
|
625 let freeBytesAvailableToUserPtr = freeBytesAvailableToUser.address(); |
|
626 |
|
627 throw_on_zero("getAvailableFreeSpace", |
|
628 WinFile.GetDiskFreeSpaceEx(sourcePath, freeBytesAvailableToUserPtr, null, null) |
|
629 ); |
|
630 |
|
631 return freeBytesAvailableToUser.value; |
|
632 }; |
|
633 |
|
634 /** |
|
635 * A global value used to receive data during time conversions. |
|
636 */ |
|
637 let gSystemTime = new Type.SystemTime.implementation(); |
|
638 let gSystemTimePtr = gSystemTime.address(); |
|
639 |
|
640 /** |
|
641 * Utility function: convert a FILETIME to a JavaScript Date. |
|
642 */ |
|
643 let FILETIME_to_Date = function FILETIME_to_Date(fileTime, path) { |
|
644 if (fileTime == null) { |
|
645 throw new TypeError("Expecting a non-null filetime"); |
|
646 } |
|
647 throw_on_zero("FILETIME_to_Date", |
|
648 WinFile.FileTimeToSystemTime(fileTime.address(), |
|
649 gSystemTimePtr), |
|
650 path); |
|
651 // Windows counts hours, minutes, seconds from UTC, |
|
652 // JS counts from local time, so we need to go through UTC. |
|
653 let utc = Date.UTC(gSystemTime.wYear, |
|
654 gSystemTime.wMonth - 1 |
|
655 /*Windows counts months from 1, JS from 0*/, |
|
656 gSystemTime.wDay, gSystemTime.wHour, |
|
657 gSystemTime.wMinute, gSystemTime.wSecond, |
|
658 gSystemTime.wMilliSeconds); |
|
659 return new Date(utc); |
|
660 }; |
|
661 |
|
662 /** |
|
663 * Utility function: convert Javascript Date to FileTime. |
|
664 * |
|
665 * @param {string} fn Name of the calling function. |
|
666 * @param {Date,number} date The date to be converted. If omitted or null, |
|
667 * then the current date will be used. If numeric, assumed to be the date |
|
668 * in milliseconds since epoch. |
|
669 */ |
|
670 let Date_to_FILETIME = function Date_to_FILETIME(fn, date, path) { |
|
671 if (typeof date === "number") { |
|
672 date = new Date(date); |
|
673 } else if (!date) { |
|
674 date = new Date(); |
|
675 } else if (typeof date.getUTCFullYear !== "function") { |
|
676 throw new TypeError("|date| parameter of " + fn + " must be a " + |
|
677 "|Date| instance or number"); |
|
678 } |
|
679 gSystemTime.wYear = date.getUTCFullYear(); |
|
680 // Windows counts months from 1, JS from 0. |
|
681 gSystemTime.wMonth = date.getUTCMonth() + 1; |
|
682 gSystemTime.wDay = date.getUTCDate(); |
|
683 gSystemTime.wHour = date.getUTCHours(); |
|
684 gSystemTime.wMinute = date.getUTCMinutes(); |
|
685 gSystemTime.wSecond = date.getUTCSeconds(); |
|
686 gSystemTime.wMilliseconds = date.getUTCMilliseconds(); |
|
687 let result = new OS.Shared.Type.FILETIME.implementation(); |
|
688 throw_on_zero("Date_to_FILETIME", |
|
689 WinFile.SystemTimeToFileTime(gSystemTimePtr, |
|
690 result.address()), |
|
691 path); |
|
692 return result; |
|
693 }; |
|
694 |
|
695 /** |
|
696 * Iterate on one directory. |
|
697 * |
|
698 * This iterator will not enter subdirectories. |
|
699 * |
|
700 * @param {string} path The directory upon which to iterate. |
|
701 * @param {*=} options An object that may contain the following field: |
|
702 * @option {string} winPattern Windows file name pattern; if set, |
|
703 * only files matching this pattern are returned. |
|
704 * |
|
705 * @throws {File.Error} If |path| does not represent a directory or |
|
706 * if the directory cannot be iterated. |
|
707 * @constructor |
|
708 */ |
|
709 File.DirectoryIterator = function DirectoryIterator(path, options) { |
|
710 exports.OS.Shared.AbstractFile.AbstractIterator.call(this); |
|
711 if (options && options.winPattern) { |
|
712 this._pattern = path + "\\" + options.winPattern; |
|
713 } else { |
|
714 this._pattern = path + "\\*"; |
|
715 } |
|
716 this._path = path; |
|
717 |
|
718 // Pre-open the first item. |
|
719 this._first = true; |
|
720 this._findData = new Type.FindData.implementation(); |
|
721 this._findDataPtr = this._findData.address(); |
|
722 this._handle = WinFile.FindFirstFile(this._pattern, this._findDataPtr); |
|
723 if (this._handle == Const.INVALID_HANDLE_VALUE) { |
|
724 let error = ctypes.winLastError; |
|
725 this._findData = null; |
|
726 this._findDataPtr = null; |
|
727 if (error == Const.ERROR_FILE_NOT_FOUND) { |
|
728 // Directory is empty, let's behave as if it were closed |
|
729 SharedAll.LOG("Directory is empty"); |
|
730 this._closed = true; |
|
731 this._exists = true; |
|
732 } else if (error == Const.ERROR_PATH_NOT_FOUND) { |
|
733 // Directory does not exist, let's throw if we attempt to walk it |
|
734 SharedAll.LOG("Directory does not exist"); |
|
735 this._closed = true; |
|
736 this._exists = false; |
|
737 } else { |
|
738 throw new File.Error("DirectoryIterator", error, this._path); |
|
739 } |
|
740 } else { |
|
741 this._closed = false; |
|
742 this._exists = true; |
|
743 } |
|
744 }; |
|
745 |
|
746 File.DirectoryIterator.prototype = Object.create(exports.OS.Shared.AbstractFile.AbstractIterator.prototype); |
|
747 |
|
748 |
|
749 /** |
|
750 * Fetch the next entry in the directory. |
|
751 * |
|
752 * @return null If we have reached the end of the directory. |
|
753 */ |
|
754 File.DirectoryIterator.prototype._next = function _next() { |
|
755 // Bailout if the directory does not exist |
|
756 if (!this._exists) { |
|
757 throw File.Error.noSuchFile("DirectoryIterator.prototype.next", this._path); |
|
758 } |
|
759 // Bailout if the iterator is closed. |
|
760 if (this._closed) { |
|
761 return null; |
|
762 } |
|
763 // If this is the first entry, we have obtained it already |
|
764 // during construction. |
|
765 if (this._first) { |
|
766 this._first = false; |
|
767 return this._findData; |
|
768 } |
|
769 |
|
770 if (WinFile.FindNextFile(this._handle, this._findDataPtr)) { |
|
771 return this._findData; |
|
772 } else { |
|
773 let error = ctypes.winLastError; |
|
774 this.close(); |
|
775 if (error == Const.ERROR_NO_MORE_FILES) { |
|
776 return null; |
|
777 } else { |
|
778 throw new File.Error("iter (FindNextFile)", error, this._path); |
|
779 } |
|
780 } |
|
781 }, |
|
782 |
|
783 /** |
|
784 * Return the next entry in the directory, if any such entry is |
|
785 * available. |
|
786 * |
|
787 * Skip special directories "." and "..". |
|
788 * |
|
789 * @return {File.Entry} The next entry in the directory. |
|
790 * @throws {StopIteration} Once all files in the directory have been |
|
791 * encountered. |
|
792 */ |
|
793 File.DirectoryIterator.prototype.next = function next() { |
|
794 // FIXME: If we start supporting "\\?\"-prefixed paths, do not forget |
|
795 // that "." and ".." are absolutely normal file names if _path starts |
|
796 // with such prefix |
|
797 for (let entry = this._next(); entry != null; entry = this._next()) { |
|
798 let name = entry.cFileName.readString(); |
|
799 if (name == "." || name == "..") { |
|
800 continue; |
|
801 } |
|
802 return new File.DirectoryIterator.Entry(entry, this._path); |
|
803 } |
|
804 throw StopIteration; |
|
805 }; |
|
806 |
|
807 File.DirectoryIterator.prototype.close = function close() { |
|
808 if (this._closed) { |
|
809 return; |
|
810 } |
|
811 this._closed = true; |
|
812 if (this._handle) { |
|
813 // We might not have a handle if the iterator is closed |
|
814 // before being used. |
|
815 throw_on_zero("FindClose", |
|
816 WinFile.FindClose(this._handle), |
|
817 this._path); |
|
818 this._handle = null; |
|
819 } |
|
820 }; |
|
821 |
|
822 /** |
|
823 * Determine whether the directory exists. |
|
824 * |
|
825 * @return {boolean} |
|
826 */ |
|
827 File.DirectoryIterator.prototype.exists = function exists() { |
|
828 return this._exists; |
|
829 }; |
|
830 |
|
831 File.DirectoryIterator.Entry = function Entry(win_entry, parent) { |
|
832 if (!win_entry.dwFileAttributes || !win_entry.ftCreationTime || |
|
833 !win_entry.ftLastAccessTime || !win_entry.ftLastWriteTime) |
|
834 throw new TypeError(); |
|
835 |
|
836 // Copy the relevant part of |win_entry| to ensure that |
|
837 // our data is not overwritten prematurely. |
|
838 let isDir = !!(win_entry.dwFileAttributes & Const.FILE_ATTRIBUTE_DIRECTORY); |
|
839 let isSymLink = !!(win_entry.dwFileAttributes & Const.FILE_ATTRIBUTE_REPARSE_POINT); |
|
840 |
|
841 let winCreationDate = FILETIME_to_Date(win_entry.ftCreationTime, this._path); |
|
842 let winLastWriteDate = FILETIME_to_Date(win_entry.ftLastWriteTime, this._path); |
|
843 let winLastAccessDate = FILETIME_to_Date(win_entry.ftLastAccessTime, this._path); |
|
844 |
|
845 let name = win_entry.cFileName.readString(); |
|
846 if (!name) { |
|
847 throw new TypeError("Empty name"); |
|
848 } |
|
849 |
|
850 if (!parent) { |
|
851 throw new TypeError("Empty parent"); |
|
852 } |
|
853 this._parent = parent; |
|
854 |
|
855 let path = Path.join(this._parent, name); |
|
856 |
|
857 SysAll.AbstractEntry.call(this, isDir, isSymLink, name, |
|
858 winCreationDate, winLastWriteDate, |
|
859 winLastAccessDate, path); |
|
860 }; |
|
861 File.DirectoryIterator.Entry.prototype = Object.create(SysAll.AbstractEntry.prototype); |
|
862 |
|
863 /** |
|
864 * Return a version of an instance of |
|
865 * File.DirectoryIterator.Entry that can be sent from a worker |
|
866 * thread to the main thread. Note that deserialization is |
|
867 * asymmetric and returns an object with a different |
|
868 * implementation. |
|
869 */ |
|
870 File.DirectoryIterator.Entry.toMsg = function toMsg(value) { |
|
871 if (!value instanceof File.DirectoryIterator.Entry) { |
|
872 throw new TypeError("parameter of " + |
|
873 "File.DirectoryIterator.Entry.toMsg must be a " + |
|
874 "File.DirectoryIterator.Entry"); |
|
875 } |
|
876 let serialized = {}; |
|
877 for (let key in File.DirectoryIterator.Entry.prototype) { |
|
878 serialized[key] = value[key]; |
|
879 } |
|
880 return serialized; |
|
881 }; |
|
882 |
|
883 |
|
884 /** |
|
885 * Information on a file. |
|
886 * |
|
887 * To obtain the latest information on a file, use |File.stat| |
|
888 * (for an unopened file) or |File.prototype.stat| (for an |
|
889 * already opened file). |
|
890 * |
|
891 * @constructor |
|
892 */ |
|
893 File.Info = function Info(stat, path) { |
|
894 let isDir = !!(stat.dwFileAttributes & Const.FILE_ATTRIBUTE_DIRECTORY); |
|
895 let isSymLink = !!(stat.dwFileAttributes & Const.FILE_ATTRIBUTE_REPARSE_POINT); |
|
896 |
|
897 let winBirthDate = FILETIME_to_Date(stat.ftCreationTime, this._path); |
|
898 let lastAccessDate = FILETIME_to_Date(stat.ftLastAccessTime, this._path); |
|
899 let lastWriteDate = FILETIME_to_Date(stat.ftLastWriteTime, this._path); |
|
900 |
|
901 let value = ctypes.UInt64.join(stat.nFileSizeHigh, stat.nFileSizeLow); |
|
902 let size = Type.uint64_t.importFromC(value); |
|
903 |
|
904 SysAll.AbstractInfo.call(this, path, isDir, isSymLink, size, |
|
905 winBirthDate, lastAccessDate, lastWriteDate); |
|
906 }; |
|
907 File.Info.prototype = Object.create(SysAll.AbstractInfo.prototype); |
|
908 |
|
909 /** |
|
910 * Return a version of an instance of File.Info that can be sent |
|
911 * from a worker thread to the main thread. Note that deserialization |
|
912 * is asymmetric and returns an object with a different implementation. |
|
913 */ |
|
914 File.Info.toMsg = function toMsg(stat) { |
|
915 if (!stat instanceof File.Info) { |
|
916 throw new TypeError("parameter of File.Info.toMsg must be a File.Info"); |
|
917 } |
|
918 let serialized = {}; |
|
919 for (let key in File.Info.prototype) { |
|
920 serialized[key] = stat[key]; |
|
921 } |
|
922 return serialized; |
|
923 }; |
|
924 |
|
925 |
|
926 /** |
|
927 * Fetch the information on a file. |
|
928 * |
|
929 * Performance note: if you have opened the file already, |
|
930 * method |File.prototype.stat| is generally much faster |
|
931 * than method |File.stat|. |
|
932 * |
|
933 * Platform-specific note: under Windows, if the file is |
|
934 * already opened without sharing of the read capability, |
|
935 * this function will fail. |
|
936 * |
|
937 * @return {File.Information} |
|
938 */ |
|
939 File.stat = function stat(path) { |
|
940 let file = File.open(path, FILE_STAT_MODE, FILE_STAT_OPTIONS); |
|
941 try { |
|
942 return file.stat(); |
|
943 } finally { |
|
944 file.close(); |
|
945 } |
|
946 }; |
|
947 // All of the following is required to ensure that File.stat |
|
948 // also works on directories. |
|
949 const FILE_STAT_MODE = { |
|
950 read: true |
|
951 }; |
|
952 const FILE_STAT_OPTIONS = { |
|
953 // Directories can be opened neither for reading(!) nor for writing |
|
954 winAccess: 0, |
|
955 // Directories can only be opened with backup semantics(!) |
|
956 winFlags: Const.FILE_FLAG_BACKUP_SEMANTICS, |
|
957 winDisposition: Const.OPEN_EXISTING |
|
958 }; |
|
959 |
|
960 /** |
|
961 * Set the file's access permission bits. |
|
962 * Not implemented for Windows (bug 1022816). |
|
963 */ |
|
964 File.setPermissions = function setPermissions(path, options = {}) { |
|
965 // do nothing |
|
966 }; |
|
967 |
|
968 /** |
|
969 * Set the last access and modification date of the file. |
|
970 * The time stamp resolution is 1 second at best, but might be worse |
|
971 * depending on the platform. |
|
972 * |
|
973 * Performance note: if you have opened the file already in write mode, |
|
974 * method |File.prototype.stat| is generally much faster |
|
975 * than method |File.stat|. |
|
976 * |
|
977 * Platform-specific note: under Windows, if the file is |
|
978 * already opened without sharing of the write capability, |
|
979 * this function will fail. |
|
980 * |
|
981 * @param {string} path The full name of the file to set the dates for. |
|
982 * @param {Date,number=} accessDate The last access date. If numeric, |
|
983 * milliseconds since epoch. If omitted or null, then the current date |
|
984 * will be used. |
|
985 * @param {Date,number=} modificationDate The last modification date. If |
|
986 * numeric, milliseconds since epoch. If omitted or null, then the current |
|
987 * date will be used. |
|
988 * |
|
989 * @throws {TypeError} In case of invalid paramters. |
|
990 * @throws {OS.File.Error} In case of I/O error. |
|
991 */ |
|
992 File.setDates = function setDates(path, accessDate, modificationDate) { |
|
993 let file = File.open(path, FILE_SETDATES_MODE, FILE_SETDATES_OPTIONS); |
|
994 try { |
|
995 return file.setDates(accessDate, modificationDate); |
|
996 } finally { |
|
997 file.close(); |
|
998 } |
|
999 }; |
|
1000 // All of the following is required to ensure that File.setDates |
|
1001 // also works on directories. |
|
1002 const FILE_SETDATES_MODE = { |
|
1003 write: true |
|
1004 }; |
|
1005 const FILE_SETDATES_OPTIONS = { |
|
1006 winAccess: Const.GENERIC_WRITE, |
|
1007 // Directories can only be opened with backup semantics(!) |
|
1008 winFlags: Const.FILE_FLAG_BACKUP_SEMANTICS, |
|
1009 winDisposition: Const.OPEN_EXISTING |
|
1010 }; |
|
1011 |
|
1012 File.read = exports.OS.Shared.AbstractFile.read; |
|
1013 File.writeAtomic = exports.OS.Shared.AbstractFile.writeAtomic; |
|
1014 File.openUnique = exports.OS.Shared.AbstractFile.openUnique; |
|
1015 File.makeDir = exports.OS.Shared.AbstractFile.makeDir; |
|
1016 |
|
1017 /** |
|
1018 * Remove an existing directory and its contents. |
|
1019 * |
|
1020 * @param {string} path The name of the directory. |
|
1021 * @param {*=} options Additional options. |
|
1022 * - {bool} ignoreAbsent If |false|, throw an error if the directory doesn't |
|
1023 * exist. |true| by default. |
|
1024 * - {boolean} ignorePermissions If |true|, remove the file even when lacking write |
|
1025 * permission. |
|
1026 * |
|
1027 * @throws {OS.File.Error} In case of I/O error, in particular if |path| is |
|
1028 * not a directory. |
|
1029 */ |
|
1030 File.removeDir = function(path, options) { |
|
1031 // We can't use File.stat here because it will follow the symlink. |
|
1032 let attributes = WinFile.GetFileAttributes(path); |
|
1033 if (attributes == Const.INVALID_FILE_ATTRIBUTES) { |
|
1034 if ((!("ignoreAbsent" in options) || options.ignoreAbsent) && |
|
1035 ctypes.winLastError == Const.ERROR_FILE_NOT_FOUND) { |
|
1036 return; |
|
1037 } |
|
1038 throw new File.Error("removeEmptyDir", ctypes.winLastError, path); |
|
1039 } |
|
1040 if (attributes & Const.FILE_ATTRIBUTE_REPARSE_POINT) { |
|
1041 // Unlike Unix symlinks, NTFS junctions or NTFS symlinks to |
|
1042 // directories are directories themselves. OS.File.remove() |
|
1043 // will not work for them. |
|
1044 OS.File.removeEmptyDir(path, options); |
|
1045 return; |
|
1046 } |
|
1047 exports.OS.Shared.AbstractFile.removeRecursive(path, options); |
|
1048 }; |
|
1049 |
|
1050 /** |
|
1051 * Get the current directory by getCurrentDirectory. |
|
1052 */ |
|
1053 File.getCurrentDirectory = function getCurrentDirectory() { |
|
1054 // This function is more complicated than one could hope. |
|
1055 // |
|
1056 // This is due to two facts: |
|
1057 // - the maximal length of a path under Windows is not completely |
|
1058 // specified (there is a constant MAX_PATH, but it is quite possible |
|
1059 // to create paths that are much larger, see bug 744413); |
|
1060 // - if we attempt to call |GetCurrentDirectory| with a buffer that |
|
1061 // is too short, it returns the length of the current directory, but |
|
1062 // this length might be insufficient by the time we can call again |
|
1063 // the function with a larger buffer, in the (unlikely but possible) |
|
1064 // case in which the process changes directory to a directory with |
|
1065 // a longer name between both calls. |
|
1066 // |
|
1067 let buffer_size = 4096; |
|
1068 while (true) { |
|
1069 let array = new (ctypes.ArrayType(ctypes.jschar, buffer_size))(); |
|
1070 let expected_size = throw_on_zero("getCurrentDirectory", |
|
1071 WinFile.GetCurrentDirectory(buffer_size, array) |
|
1072 ); |
|
1073 if (expected_size <= buffer_size) { |
|
1074 return array.readString(); |
|
1075 } |
|
1076 // At this point, we are in a case in which our buffer was not |
|
1077 // large enough to hold the name of the current directory. |
|
1078 // Consequently, we need to increase the size of the buffer. |
|
1079 // Note that, even in crazy scenarios, the loop will eventually |
|
1080 // converge, as the length of the paths cannot increase infinitely. |
|
1081 buffer_size = expected_size + 1 /* to store \0 */; |
|
1082 } |
|
1083 }; |
|
1084 |
|
1085 /** |
|
1086 * Set the current directory by setCurrentDirectory. |
|
1087 */ |
|
1088 File.setCurrentDirectory = function setCurrentDirectory(path) { |
|
1089 throw_on_zero("setCurrentDirectory", |
|
1090 WinFile.SetCurrentDirectory(path), |
|
1091 path); |
|
1092 }; |
|
1093 |
|
1094 /** |
|
1095 * Get/set the current directory by |curDir|. |
|
1096 */ |
|
1097 Object.defineProperty(File, "curDir", { |
|
1098 set: function(path) { |
|
1099 this.setCurrentDirectory(path); |
|
1100 }, |
|
1101 get: function() { |
|
1102 return this.getCurrentDirectory(); |
|
1103 } |
|
1104 } |
|
1105 ); |
|
1106 |
|
1107 // Utility functions, used for error-handling |
|
1108 |
|
1109 /** |
|
1110 * Turn the result of |open| into an Error or a File |
|
1111 * @param {number} maybe The result of the |open| operation that may |
|
1112 * represent either an error or a success. If -1, this function raises |
|
1113 * an error holding ctypes.winLastError, otherwise it returns the opened file. |
|
1114 * @param {string=} path The path of the file. |
|
1115 */ |
|
1116 function error_or_file(maybe, path) { |
|
1117 if (maybe == Const.INVALID_HANDLE_VALUE) { |
|
1118 throw new File.Error("open", ctypes.winLastError, path); |
|
1119 } |
|
1120 return new File(maybe, path); |
|
1121 } |
|
1122 |
|
1123 /** |
|
1124 * Utility function to sort errors represented as "0" from successes. |
|
1125 * |
|
1126 * @param {string=} operation The name of the operation. If unspecified, |
|
1127 * the name of the caller function. |
|
1128 * @param {number} result The result of the operation that may |
|
1129 * represent either an error or a success. If 0, this function raises |
|
1130 * an error holding ctypes.winLastError, otherwise it returns |result|. |
|
1131 * @param {string=} path The path of the file. |
|
1132 */ |
|
1133 function throw_on_zero(operation, result, path) { |
|
1134 if (result == 0) { |
|
1135 throw new File.Error(operation, ctypes.winLastError, path); |
|
1136 } |
|
1137 return result; |
|
1138 } |
|
1139 |
|
1140 /** |
|
1141 * Utility function to sort errors represented as "-1" from successes. |
|
1142 * |
|
1143 * @param {string=} operation The name of the operation. If unspecified, |
|
1144 * the name of the caller function. |
|
1145 * @param {number} result The result of the operation that may |
|
1146 * represent either an error or a success. If -1, this function raises |
|
1147 * an error holding ctypes.winLastError, otherwise it returns |result|. |
|
1148 * @param {string=} path The path of the file. |
|
1149 */ |
|
1150 function throw_on_negative(operation, result, path) { |
|
1151 if (result < 0) { |
|
1152 throw new File.Error(operation, ctypes.winLastError, path); |
|
1153 } |
|
1154 return result; |
|
1155 } |
|
1156 |
|
1157 /** |
|
1158 * Utility function to sort errors represented as |null| from successes. |
|
1159 * |
|
1160 * @param {string=} operation The name of the operation. If unspecified, |
|
1161 * the name of the caller function. |
|
1162 * @param {pointer} result The result of the operation that may |
|
1163 * represent either an error or a success. If |null|, this function raises |
|
1164 * an error holding ctypes.winLastError, otherwise it returns |result|. |
|
1165 * @param {string=} path The path of the file. |
|
1166 */ |
|
1167 function throw_on_null(operation, result, path) { |
|
1168 if (result == null || (result.isNull && result.isNull())) { |
|
1169 throw new File.Error(operation, ctypes.winLastError, path); |
|
1170 } |
|
1171 return result; |
|
1172 } |
|
1173 |
|
1174 File.Win = exports.OS.Win.File; |
|
1175 File.Error = SysAll.Error; |
|
1176 exports.OS.File = File; |
|
1177 exports.OS.Shared.Type = Type; |
|
1178 |
|
1179 Object.defineProperty(File, "POS_START", { value: SysAll.POS_START }); |
|
1180 Object.defineProperty(File, "POS_CURRENT", { value: SysAll.POS_CURRENT }); |
|
1181 Object.defineProperty(File, "POS_END", { value: SysAll.POS_END }); |
|
1182 })(this); |
|
1183 } |