2 * Copyright (c) 2001, Robert Collins.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * A copy of the GNU General Public License can be found at
12 * Written by Robert Collins <rbtcollins@hotmail.com>
18 #include "filemanip.h"
21 #include "compactos.h"
23 #include "getopt++/StringChoiceOption.h"
29 #include "io_stream_cygfile.h"
30 #include "IOStreamProvider.h"
31 #include "LogSingleton.h"
33 static StringChoiceOption::StringChoices
algs({
34 {"xpress4k", FILE_PROVIDER_COMPRESSION_XPRESS4K
},
35 {"xpress8k", FILE_PROVIDER_COMPRESSION_XPRESS8K
},
36 {"xpress16k", FILE_PROVIDER_COMPRESSION_XPRESS16K
},
37 {"lzx", FILE_PROVIDER_COMPRESSION_LZX
},
40 static StringChoiceOption
CompactOsOption(algs
,
42 "Compress installed files with Compact OS (xpress4k, xpress8k, xpress16k, lzx)",
43 true, -1, FILE_PROVIDER_COMPRESSION_LZX
);
45 /* completely private iostream registration class */
46 class CygFileProvider
: public IOStreamProvider
49 int exists (const std::string
& path
) const
50 {return io_stream_cygfile::exists(path
);}
51 int remove (const std::string
& path
) const
52 {return io_stream_cygfile::remove(path
);}
53 int mklink (const std::string
& a
, const std::string
& b
, io_stream_link_t c
) const
54 {return io_stream_cygfile::mklink(a
,b
,c
);}
55 io_stream
*open (const std::string
& a
,const std::string
& b
, mode_t m
) const
56 {return new io_stream_cygfile (a
, b
, m
);}
58 int move (const std::string
& a
,const std::string
& b
) const
59 {return io_stream_cygfile::move (a
, b
);}
60 int mkdir_p (path_type_t isadir
, const std::string
& path
, mode_t mode
) const
61 {return cygmkdir_p (isadir
, path
, mode
);}
63 CygFileProvider() // no creating this
65 io_stream::registerProvider (theInstance
, "cygfile://");
67 CygFileProvider(CygFileProvider
const &); // no copying
68 CygFileProvider
&operator=(CygFileProvider
const &); // no assignment
70 static CygFileProvider theInstance
;
72 CygFileProvider
CygFileProvider::theInstance
= CygFileProvider();
75 std::string
io_stream_cygfile::cwd("/");
76 bool io_stream_cygfile::compact_os_is_available
= (OSMajorVersion () >= 10);
78 // Normalise a unix style path relative to
81 io_stream_cygfile::normalise (const std::string
& unixpath
)
85 if (unixpath
.c_str()[0]=='/')
88 path
= new_cstr_char_array (unixpath
);
89 tempout
= new_cstr_char_array (unixpath
); // paths only shrink.
93 path
= new_cstr_char_array (cwd
+ unixpath
);
94 tempout
= new_cstr_char_array (cwd
+ unixpath
); //paths only shrink.
97 // FIXME: handle .. depth tests to prevent / + ../foo/ stepping out
99 // FIXME: handle /./ sequences
100 bool sawslash
= false;
101 char *outptr
= tempout
;
102 for (char *ptr
=path
; *ptr
; ++ptr
)
104 if (*ptr
== '/' && sawslash
)
106 else if (*ptr
== '/')
112 std::string rv
= tempout
;
119 io_stream_cygfile::w_str ()
123 wname
= new wchar_t [fname
.size () + 7];
125 mklongpath (wname
, fname
.c_str (), fname
.size () + 7);
133 if (get_root_dir ().size())
135 read_mounts (std::string ());
139 compactos_is_useless (const std::string
& name
)
141 const char * const p
= name
.c_str();
142 if (!(!strncmp (p
, "/bin/", 5) || !strncmp (p
, "/sbin/", 6) || !strncmp (p
, "/usr/", 5)))
143 return true; /* File is not in R/O tree. */
144 const size_t len
= name
.size(); /* >= 5 */
145 if (!strcmp (p
+ (len
- 4), ".dll") || !strcmp (p
+ (len
- 3), ".so")) {
146 if ((len
>= 5 + 11 && !strcmp (p
+ (len
- 11), "cygwin1.dll"))
147 || strstr (p
+ 5, "/sys-root/mingw/"))
148 return false; /* Ignored by rebase. */
149 return true; /* Rebase will open file for writing which uncompresses the file. */
151 if (!strcmp (p
+ (len
- 4), ".bz2") || !strcmp (p
+ (len
- 3), ".gz")
152 || !strcmp (p
+ (len
- 3), ".xz"))
153 return true; /* File is already compressed. */
157 io_stream_cygfile::io_stream_cygfile (const std::string
& name
, const std::string
& mode
, mode_t perms
)
158 : fp(), lasterr (0), fname(), wname (NULL
), compact_os_algorithm(-1)
163 Log (LOG_TIMESTAMP
) << "io_stream_cygfile: Bad parameters" << endLog
;
167 /* do this every time because the mount points may change due to fwd/back button use...
168 * TODO: make this less...manual
171 if (!get_root_dir ().size())
173 /* TODO: assign a errno for "no mount table :} " */
174 Log (LOG_TIMESTAMP
) << "io_stream_cygfile: Error reading mounts" << endLog
;
178 fname
= cygpath (normalise(name
));
181 if (fname
.rfind (".exe") != std::string::npos
182 || fname
.rfind (".dll") != std::string::npos
)
183 perms
|= 0111; /* Make .exe and .dll always executable. */
184 fp
= nt_wfopen (w_str(), mode
.c_str (), perms
);
188 Log (LOG_TIMESTAMP
) << "io_stream_cygfile: fopen(" << name
<< ") failed " << errno
<< " "
189 << strerror(errno
) << endLog
;
192 if (mode
[0] == 'w' && compact_os_is_available
&& CompactOsOption
>= 0
193 && !compactos_is_useless (name
))
194 compact_os_algorithm
= CompactOsOption
;
198 io_stream_cygfile::~io_stream_cygfile ()
208 io_stream_cygfile::exists (const std::string
& path
)
211 if (!get_root_dir ().size())
214 size_t len
= cygpath (normalise(path
)).size () + 7;
216 mklongpath (wname
, cygpath (normalise(path
)).c_str (), len
);
217 DWORD attr
= GetFileAttributesW (wname
);
218 if (attr
!= INVALID_FILE_ATTRIBUTES
)
224 io_stream_cygfile::remove (const std::string
& path
)
229 if (!get_root_dir ().size())
230 /* TODO: assign a errno for "no mount table :} " */
233 size_t len
= cygpath (normalise(path
)).size () + 7;
235 mklongpath (wpath
, cygpath (normalise(path
)).c_str (), len
);
237 unsigned long w
= GetFileAttributesW (wpath
);
238 if (w
!= INVALID_FILE_ATTRIBUTES
&& w
& FILE_ATTRIBUTE_DIRECTORY
)
240 len
= wcslen (wpath
);
247 swprintf (tmp
+ len
, L
"old-%d", i
);
249 while (GetFileAttributesW (tmp
) != INVALID_FILE_ATTRIBUTES
);
250 Log (LOG_TIMESTAMP
) << "warning: moving directory \"" << normalise(path
).c_str()
251 << "\" out of the way." << endLog
;
252 MoveFileW (wpath
, tmp
);
254 return io_stream::remove (std::string ("file://") + cygpath (normalise(path
)).c_str());
257 /* Returns 0 for success */
259 io_stream_cygfile::mklink (const std::string
& _from
, const std::string
& _to
,
260 io_stream_link_t linktype
)
262 if (!_from
.size() || !_to
.size())
264 std::string
from(normalise(_from
));
265 std::string
to (normalise(_to
));
268 case IO_STREAM_SYMLINK
:
269 // symlinks are arbitrary targets, can be anything, and are
270 // not subject to translation
271 return mkcygsymlink (cygpath (from
).c_str(), _to
.c_str());
272 case IO_STREAM_HARDLINK
:
274 /* First try to create a real hardlink. */
275 if (!mkcyghardlink (cygpath (from
).c_str(), cygpath (to
).c_str ()))
278 /* If creating a hardlink failed, we're probably on a filesystem
279 which doesn't support hardlinks. If so, we also don't care for
280 permissions for now. The filesystem is probably a filesystem
281 which doesn't support ACLs anyway. */
283 /* textmode alert: should we translate when linking from an binmode to a
284 text mode mount and vice verca?
286 io_stream
*in
= io_stream::open (std::string ("cygfile://") + to
, "rb", 0);
289 Log (LOG_TIMESTAMP
) << "could not open " << to
290 << " for reading in mklink" << endLog
;
293 io_stream
*out
= io_stream::open (std::string ("cygfile://") + from
, "wb", 0644);
296 Log (LOG_TIMESTAMP
) << "could not open " << from
297 << " for writing in mklink" << endLog
;
302 if (io_stream::copy (in
, out
))
304 Log (LOG_TIMESTAMP
) << "Failed to hardlink " << from
<< "->"
305 << to
<< " during file copy." << endLog
;
322 io_stream_cygfile::read (void *buffer
, size_t len
)
325 return fread (buffer
, 1, len
, fp
);
330 io_stream_cygfile::write (const void *buffer
, size_t len
)
333 return fwrite (buffer
, 1, len
, fp
);
338 io_stream_cygfile::peek (void *buffer
, size_t len
)
342 int pos
= ftell (fp
);
343 ssize_t rv
= fread (buffer
, 1, len
, fp
);
344 fseek (fp
, pos
, SEEK_SET
);
351 io_stream_cygfile::tell ()
361 io_stream_cygfile::seek (long where
, io_stream_seek_t whence
)
365 return fseek (fp
, where
, (int) whence
);
372 io_stream_cygfile::error ()
380 cygmkdir_p (path_type_t isadir
, const std::string
& _name
, mode_t mode
)
384 std::string
name(io_stream_cygfile::normalise(_name
));
386 if (!get_root_dir ().size())
387 /* TODO: assign a errno for "no mount table :} " */
389 return mkdir_p (isadir
== PATH_TO_DIR
? 1 : 0, cygpath (name
).c_str(), mode
);
393 io_stream_cygfile::set_mtime (time_t mtime
)
399 long long ftimev
= mtime
* NSPERSEC
+ FACTOR
;
401 ftime
.dwHighDateTime
= ftimev
>> 32;
402 ftime
.dwLowDateTime
= ftimev
;
404 h
= CreateFileW (w_str (), GENERIC_WRITE
,
405 FILE_SHARE_READ
| FILE_SHARE_WRITE
, 0, OPEN_EXISTING
,
406 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_BACKUP_SEMANTICS
, 0);
407 if (h
== INVALID_HANDLE_VALUE
)
410 if (compact_os_algorithm
>= 0)
412 /* Compact OS must be applied after last WriteFile()
413 and before SetFileTime(). */
414 int rc
= CompactOsCompressFile (h
, compact_os_algorithm
);
417 DWORD err
= GetLastError();
418 Log (LOG_TIMESTAMP
) << "Compact OS disabled after error " << err
419 << " on " << fname
<< endLog
;
420 compact_os_is_available
= false;
423 Log (LOG_BABBLE
) << "Compact OS algorithm " << compact_os_algorithm
424 << (rc
== 0 ? " not" : "") << " applied to " << fname
<< endLog
;
427 SetFileTime (h
, 0, 0, &ftime
);
433 io_stream_cygfile::move (const std::string
& _from
, const std::string
& _to
)
435 if (!_from
.size() || !_to
.size())
437 std::string
from (normalise(_from
));
438 std::string
to(normalise(_to
));
440 if (!get_root_dir ().size())
441 /* TODO: assign a errno for "no mount table :} " */
443 return rename (cygpath (from
).c_str(), cygpath (to
).c_str());
447 io_stream_cygfile::get_size ()
453 h
= CreateFileW (w_str (), GENERIC_READ
,
454 FILE_SHARE_READ
| FILE_SHARE_WRITE
, 0, OPEN_EXISTING
,
455 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_BACKUP_SEMANTICS
, 0);
456 if (h
!= INVALID_HANDLE_VALUE
)
458 ret
= GetFileSize (h
, NULL
);