1 /*******************************************************************************
2 
3         Copyright:
4             Copyright (c) 2004 Kris Bell.
5             Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH.
6             All rights reserved.
7 
8         License:
9             Tango Dual License: 3-Clause BSD License / Academic Free License v3.0.
10             See LICENSE_TANGO.txt for details.
11 
12         Version:
13             Mar 2004: Initial release$(BR)
14             Dec 2006: Outback release$(BR)
15             Nov 2008: relocated and simplified
16 
17         Authors:
18             Kris,
19             John Reimer,
20             Anders F Bjorklund (Darwin patches),
21             Chris Sauls (Win95 file support)
22 
23 *******************************************************************************/
24 
25 module ocean.io.device.File;
26 
27 import ArrayMut = ocean.core.array.Mutation;
28 import ocean.meta.types.Qualifiers;
29 import ocean.core.Verify;
30 
31 import ocean.sys.Common;
32 import ocean.text.util.StringC;
33 import ocean.io.device.Device;
34 import ocean.stdc.gnu.string;
35 
36 import core.stdc.string;
37 import core.stdc.errno;
38 
39 /*******************************************************************************
40 
41         platform-specific functions
42 
43 *******************************************************************************/
44 
45 import core.sys.posix.unistd;
46 import core.sys.posix.sys.types;
47 
48 /*******************************************************************************
49 
50         Implements a means of reading and writing a generic file. Conduits
51         are the primary means of accessing external data, and File
52         extends the basic pattern by providing file-specific methods to
53         set the file size, seek to a specific file position and so on.
54 
55         Serial input and output is straightforward. In this example we
56         copy a file directly to the console:
57         ---
58         // open a file for reading
59         auto from = new File ("test.txt");
60 
61         // stream directly to console
62         Stdout.copy (from);
63         ---
64 
65         And here we copy one file to another:
66         ---
67         // open file for reading
68         auto from = new File ("test.txt");
69 
70         // open another for writing
71         auto to = new File ("copy.txt", File.WriteCreate);
72 
73         // copy file and close
74         to.copy.close;
75         from.close;
76         ---
77 
78         You can use InputStream.load() to load a file directly into memory:
79         ---
80         auto file = new File ("test.txt");
81         auto content = file.load;
82         file.close;
83         ---
84 
85         Or use a convenience static function within File:
86         ---
87         auto content = File.get ("test.txt");
88         ---
89 
90         A more explicit version with a similar result would be:
91         ---
92         // open file for reading
93         auto file = new File ("test.txt");
94 
95         // create an array to house the entire file
96         auto content = new char [file.length];
97 
98         // read the file content. Return value is the number of bytes read
99         auto bytes = file.read (content);
100         file.close;
101         ---
102 
103         Conversely, one may write directly to a File like so:
104         ---
105         // open file for writing
106         auto to = new File ("text.txt", File.WriteCreate);
107 
108         // write an array of content to it
109         auto bytes = to.write (content);
110         ---
111 
112         There are equivalent static functions, File.set() and
113         File.append(), which set or append file content respectively.
114 
115         File can happily handle random I/O. Here we use seek() to
116         relocate the file pointer:
117         ---
118         // open a file for reading and writing
119         auto file = new File ("random.bin", File.ReadWriteCreate);
120 
121         // write some data
122         file.write ("testing");
123 
124         // rewind to file start
125         file.seek (0);
126 
127         // read data back again
128         char[10] tmp;
129         auto bytes = file.read (tmp);
130 
131         file.close;
132         ---
133 
134         Note that File is unbuffered by default - wrap an instance within
135         ocean.io.stream.Buffered for buffered I/O.
136 
137         Compile with -version=Win32SansUnicode to enable Win95 & Win32s file
138         support.
139 
140 *******************************************************************************/
141 
142 class File : Device, Device.Seek, Device.Truncate
143 {
144         static import ocean.core.ExceptionDefinitions;
145         import ocean.sys.CloseOnExec;
146         static immutable O_CLOEXEC = 0x80000;
147 
148         public alias Device.read  read;
149         public alias Device.write write;
150 
151         /***********************************************************************
152 
153             Exception class thrown on errors.
154 
155         ***********************************************************************/
156 
157         public static class IOException: ocean.core.ExceptionDefinitions.IOException
158         {
159             import ocean.core.Exception: ReusableExceptionImplementation;
160 
161             mixin ReusableExceptionImplementation!() ReusableImpl;
162 
163             /*******************************************************************
164 
165                 Sets the exception instance.
166 
167                 Params:
168                     file_path = path of the file
169                     error_num = error code (defaults to .errno)
170                     func_name = name of the method that failed
171                     msg = message description of the error (uses stderr if empty)
172                     file = file where exception is thrown
173                     line = line where exception is thrown
174 
175             *******************************************************************/
176 
177 
178             public typeof(this) set ( cstring file_path,
179                     int error_num, istring func_name = "",
180                     istring msg = "",
181                     istring file = __FILE__, long line = __LINE__)
182             {
183                 this.error_num = error_num;
184                 this.func_name = func_name;
185 
186                 this.ReusableImpl.set(this.func_name, file, line);
187 
188                 if (this.func_name.length)
189                     this.ReusableImpl.append(": ");
190 
191                 if (msg.length == 0)
192                 {
193                     char[256] buf;
194                     auto errmsg = StringC.toDString(
195                         strerror_r(this.error_num, buf.ptr, buf.length));
196 
197                     this.ReusableImpl.append(errmsg);
198                 }
199                 else
200                 {
201                     this.ReusableImpl.append(msg);
202                 }
203 
204                 this.ReusableImpl.append(" on ").append(file_path);
205 
206                 return this;
207             }
208         }
209 
210         /***********************************************************************
211 
212                 Fits into 32 bits ...
213 
214         ***********************************************************************/
215 
216         align(1) struct Style
217         {
218                 align(1):
219                 Access          access;                 /// Access rights.
220                 Open            open;                   /// How to open.
221                 Share           share;                  /// How to share.
222                 Cache           cache;                  /// How to cache.
223         }
224 
225         /***********************************************************************
226 
227         ***********************************************************************/
228 
229         enum Access : ubyte     {
230                                 Read      = 0x01,       /// Is readable.
231                                 Write     = 0x02,       /// Is writable.
232                                 ReadWrite = 0x03,       /// Both.
233                                 }
234 
235         /***********************************************************************
236 
237         ***********************************************************************/
238 
239         enum Open : ubyte       {
240                                 Exists=0,               /// Must exist.
241                                 Create,                 /// Create or truncate.
242                                 Sedate,                 /// Create if necessary.
243                                 Append,                 /// Create if necessary.
244                                 New,                    /// Can't exist.
245                                 };
246 
247         /***********************************************************************
248 
249         ***********************************************************************/
250 
251         enum Share : ubyte      {
252                                 None=0,                 /// No sharing.
253                                 Read,                   /// Shared reading.
254                                 ReadWrite,              /// Open for anything.
255                                 };
256 
257         /***********************************************************************
258 
259         ***********************************************************************/
260 
261         enum Cache : ubyte      {
262                                 None      = 0x00,       /// Don't optimize.
263                                 Random    = 0x01,       /// Optimize for random.
264                                 Stream    = 0x02,       /// Optimize for stream.
265                                 WriteThru = 0x04,       /// Backing-cache flag.
266                                 };
267 
268         /***********************************************************************
269 
270             Read an existing file.
271 
272         ***********************************************************************/
273 
274         static immutable Style ReadExisting = {Access.Read, Open.Exists};
275 
276         /***********************************************************************
277 
278             Read an existing file.
279 
280         ***********************************************************************/
281 
282         static immutable Style ReadShared = {Access.Read, Open.Exists, Share.Read};
283 
284         /***********************************************************************
285 
286             Write on an existing file. Do not create.
287 
288         ***********************************************************************/
289 
290         static immutable Style WriteExisting = {Access.Write, Open.Exists};
291 
292         /***********************************************************************
293 
294                 Write on a clean file. Create if necessary.
295 
296         ***********************************************************************/
297 
298         static immutable Style WriteCreate = {Access.Write, Open.Create};
299 
300         /***********************************************************************
301 
302                 Read from the beginning, append at the end of the file.
303 
304         ***********************************************************************/
305 
306         static immutable Style ReadWriteAppending = {Access.ReadWrite, Open.Append};
307 
308         /***********************************************************************
309 
310                 Write at the end of the file.
311 
312         ***********************************************************************/
313 
314         static immutable Style WriteAppending = {Access.Write, Open.Append};
315 
316         /***********************************************************************
317 
318                 Read and write an existing file.
319 
320         ***********************************************************************/
321 
322         static immutable Style ReadWriteExisting = {Access.ReadWrite, Open.Exists};
323 
324         /***********************************************************************
325 
326                 Read & write on a clean file. Create if necessary.
327 
328         ***********************************************************************/
329 
330         static immutable Style ReadWriteCreate = {Access.ReadWrite, Open.Create};
331 
332         /***********************************************************************
333 
334                 Read and Write. Use existing file if present.
335 
336         ***********************************************************************/
337 
338         static immutable Style ReadWriteOpen = {Access.ReadWrite, Open.Sedate};
339 
340 
341         // the file we're working with
342         private cstring  path_;
343 
344         // the style we're opened with
345         private Style   style_;
346 
347         /***********************************************************************
348 
349             Reusable exception instance
350 
351         ***********************************************************************/
352 
353         private IOException exception;
354 
355         /***********************************************************************
356 
357                 Create a File for use with open().
358 
359                 Note that File is unbuffered by default - wrap an instance
360                 within ocean.io.stream.Buffered for buffered I/O.
361 
362                 Params:
363                     exception = reusable exception instance to use, or null
364                     to create a fresh one.
365 
366         ***********************************************************************/
367 
368         this (IOException exception = null)
369         {
370             this.exception = exception;
371         }
372 
373         /***********************************************************************
374 
375                 Create a File with the provided path and style.
376 
377                 Note that File is unbuffered by default - wrap an instance
378                 within ocean.io.stream.Buffered for buffered I/O.
379 
380                 Params:
381                     exception = reusable exception instance to use, or null
382                     to create a fresh one.
383 
384         ***********************************************************************/
385 
386         this (cstring path, Style style = ReadExisting,
387                 IOException exception = null)
388         {
389                 this(exception);
390                 open (path, style);
391         }
392 
393         /***********************************************************************
394 
395                 Return the Style used for this file.
396 
397         ***********************************************************************/
398 
399         Style style ()
400         {
401                 return style_;
402         }
403 
404         /***********************************************************************
405 
406                 Return the path used by this file.
407 
408         ***********************************************************************/
409 
410         override istring toString ()
411         {
412                 return idup(path_);
413         }
414 
415         /**********************************************************************
416 
417             Returns:
418                 path used by this file.
419 
420         **********************************************************************/
421 
422         public cstring path ()
423         {
424             return this.path_;
425         }
426 
427         /***********************************************************************
428 
429                 Convenience function to return the content of a file.
430 
431         ***********************************************************************/
432 
433         static void[] get (cstring path)
434         {
435             void[] dst;
436             return get(path, dst);
437         }
438 
439         /***********************************************************************
440 
441                 Convenience function to return the content of a file.
442 
443                 This overload takes a slice to a reusable buffer which is
444                 expanded as needed.
445                 Content size is determined via the file-system, per
446                 File.length, although that may be misleading for some
447                 *nix systems. An alternative is to use File.load which
448                 loads content until an Eof is encountered.
449 
450 
451         ***********************************************************************/
452         static void[] get (cstring path, ref void[] dst)
453         {
454                 scope file = new File (path);
455 
456                 // allocate enough space for the entire file
457                 auto len = cast(size_t) file.length;
458                 if (dst.length < len){
459                     if (dst is null){ // avoid setting the noscan attribute, one should maybe change the return type
460                         dst=new ubyte[](len);
461                     } else {
462                         dst.length = len;
463                         assumeSafeAppend(dst);
464                     }
465                 }
466 
467                 //read the content
468                 len = file.read (dst);
469                 if (len is file.Eof)
470                     file.error ("File.read :: unexpected eof");
471 
472                 return dst [0 .. len];
473         }
474 
475         /***********************************************************************
476 
477                 Convenience function to set file content and length to
478                 reflect the given array.
479 
480         ***********************************************************************/
481 
482         static void set (cstring path, const(void)[] content)
483         {
484                 scope file = new File (path, ReadWriteCreate);
485                 file.write (content);
486         }
487 
488         /***********************************************************************
489 
490                 Convenience function to append content to a file.
491 
492         ***********************************************************************/
493 
494         static void append (cstring path, const(void)[] content)
495         {
496                 scope file = new File (path, WriteAppending);
497                 file.write (content);
498         }
499 
500 
501         /***********************************************************************
502 
503             Low level open for sub-classes that need to apply specific attributes.
504 
505             Return:
506                 False in case of failure.
507 
508         ***********************************************************************/
509 
510         protected bool open (cstring path, Style style,
511                              int addflags, int access = 0x1B6 /* octal 0666 */)
512         {
513             alias int[] Flags;
514 
515             static immutable Flags Access =
516                 [
517                     0,                      // invalid
518                     O_RDONLY,
519                     O_WRONLY,
520                     O_RDWR,
521                     ];
522 
523             static immutable Flags Create =
524                 [
525                     0,                      // open existing
526                     O_CREAT | O_TRUNC,      // truncate always
527                     O_CREAT,                // create if needed
528                     O_APPEND | O_CREAT,     // append
529                     O_CREAT | O_EXCL,       // can't exist
530                     ];
531 
532             static immutable short[] Locks =
533                 [
534                     F_WRLCK,                // no sharing
535                     F_RDLCK,                // shared read
536                     ];
537 
538             // remember our settings
539             verify(path !is null);
540             path_ = path;
541             style_ = style;
542 
543             // zero terminate and convert to utf16
544             static mstring buffer;
545             ArrayMut.copy(buffer, path);
546             auto name = StringC.toCString(buffer);
547             auto mode = Access[style.access] | Create[style.open];
548 
549             handle = posix.open (name, mode | setCloExec(addflags, O_CLOEXEC), access);
550             if (handle is -1)
551                 return false;
552 
553             return true;
554         }
555 
556         /***********************************************************************
557 
558             Open a file with the provided style.
559 
560             Note that files default to no-sharing. That is, they are locked
561             exclusively to the host process unless otherwise stipulated.
562             We do this in order to expose the same default behaviour as Win32.
563 
564             $(B No file locking for borked POSIX.)
565 
566         ***********************************************************************/
567 
568         void open (cstring path, Style style = ReadExisting)
569         {
570             if (! open (path, style, 0))
571                 error(.errno, "open");
572         }
573 
574         /***********************************************************************
575 
576             Wraps the already open file descriptor into a File instance.
577 
578             Params:
579                 fd = file descriptor to wrap the file around
580 
581         ***********************************************************************/
582 
583         void setFileHandle ( int fd )
584         {
585             verify (fd > 0);
586             this.handle = fd;
587         }
588 
589         /***********************************************************************
590 
591             Set the file size to be that of the current seek position.
592             The file must be writable for this to succeed.
593 
594         ***********************************************************************/
595 
596         void truncate ()
597         {
598             truncate (position);
599         }
600 
601         /***********************************************************************
602 
603             Set the file size to be the specified length.
604             The file must be writable for this to succeed.
605 
606         ***********************************************************************/
607 
608         override void truncate (long size)
609         {
610             // set filesize to be current seek-position
611             if (posix.ftruncate (handle, cast(off_t) size) is -1)
612                 error(.errno, "ftruncate");
613         }
614 
615         /***********************************************************************
616 
617             Set the file seek position to the specified offset
618             from the given anchor.
619 
620         ***********************************************************************/
621 
622         override long seek (long offset, Anchor anchor = Anchor.Begin)
623         {
624             long result = posix.lseek (handle, cast(off_t) offset, anchor);
625             if (result is -1)
626                 error(.errno, "seek");
627             return result;
628         }
629 
630         /***********************************************************************
631 
632             Return the current file position.
633 
634         ***********************************************************************/
635 
636         long position ()
637         {
638             return seek (0, Anchor.Current);
639         }
640 
641         /***********************************************************************
642 
643             Return the total length of this file.
644 
645         ***********************************************************************/
646 
647         long length ()
648         {
649             stat_t stats = void;
650             if (posix.fstat (handle, &stats))
651                 error(.errno, "fstat");
652             return cast(long) stats.st_size;
653         }
654 
655         /***********************************************************************
656 
657             Instructs the OS to flush it's internal buffers to the disk device.
658 
659             NOTE: due to OS and hardware design, data flushed
660             cannot be guaranteed to be actually on disk-platters.
661             Actual durability of data depends on write-caches,
662             barriers, presence of battery-backup, filesystem and
663             OS-support.
664 
665         ***********************************************************************/
666 
667         void sync ()
668         {
669             if (fsync (handle))
670                 error(.errno, "fsync");
671         }
672 
673         /*******************************************************************
674 
675             Throw a potentially reusable IOException, with the provided
676             message, function name and error code.
677 
678             Params:
679                 error_num = error code
680                 func_name = name of the method that failed
681                 msg = message description of the error (uses stderr if empty)
682                 file = file where exception is thrown
683                 line = line where exception is thrown
684 
685         *******************************************************************/
686 
687         public override void error ( int error_code, istring func_name,
688                 istring msg = "", istring file = __FILE__, long line = __LINE__ )
689         {
690             if (this.exception is null)
691             {
692                 this.exception = new IOException;
693             }
694 
695             throw this.exception.set(this.path_, error_code, func_name, msg, file, line);
696         }
697 
698         /***********************************************************************
699 
700                 Throw an IOException, with the provided message.
701 
702         ***********************************************************************/
703 
704         public alias Conduit.error error;
705 
706         /***********************************************************************
707 
708                 Throw an IOException noting the last error.
709 
710         ***********************************************************************/
711 
712         public alias Device.error error;
713 }