]> cygwin.com Git - cygwin-apps/setup.git/blob - io_stream_cygfile.cc
1ee55f5af4ddabb5329a486a806714e5260dea25
[cygwin-apps/setup.git] / io_stream_cygfile.cc
1 /*
2 * Copyright (c) 2001, Robert Collins.
3 *
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.
8 *
9 * A copy of the GNU General Public License can be found at
10 * http://www.gnu.org/
11 *
12 * Written by Robert Collins <rbtcollins@hotmail.com>
13 *
14 */
15
16 #include "win32.h"
17 #include "mklink2.h"
18 #include "filemanip.h"
19 #include "mkdir.h"
20 #include "mount.h"
21 #include "compactos.h"
22
23 #include "getopt++/StringChoiceOption.h"
24
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <unistd.h>
28
29 #include "io_stream_cygfile.h"
30 #include "IOStreamProvider.h"
31 #include "LogSingleton.h"
32 #include "resource.h"
33
34 static StringChoiceOption::StringChoices algs({
35 {"xpress4k", FILE_PROVIDER_COMPRESSION_XPRESS4K},
36 {"xpress8k", FILE_PROVIDER_COMPRESSION_XPRESS8K},
37 {"xpress16k", FILE_PROVIDER_COMPRESSION_XPRESS16K},
38 {"lzx", FILE_PROVIDER_COMPRESSION_LZX},
39 });
40
41 static StringChoiceOption CompactOsOption(algs,
42 '\0', "compact-os", IDS_HELPTEXT_COMPACTOS,
43 true, -1, FILE_PROVIDER_COMPRESSION_LZX);
44
45 /* completely private iostream registration class */
46 class CygFileProvider : public IOStreamProvider
47 {
48 public:
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);}
57 ~CygFileProvider (){}
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);}
62 protected:
63 CygFileProvider() // no creating this
64 {
65 io_stream::registerProvider (theInstance, "cygfile://");
66 }
67 CygFileProvider(CygFileProvider const &); // no copying
68 CygFileProvider &operator=(CygFileProvider const &); // no assignment
69 private:
70 static CygFileProvider theInstance;
71 };
72 CygFileProvider CygFileProvider::theInstance = CygFileProvider();
73
74
75 std::string io_stream_cygfile::cwd("/");
76 bool io_stream_cygfile::compact_os_is_available = (OSMajorVersion () >= 10);
77
78 // Normalise a unix style path relative to
79 // cwd.
80 std::string
81 io_stream_cygfile::normalise (const std::string& unixpath)
82 {
83 char *path,*tempout;
84
85 if (unixpath.c_str()[0]=='/')
86 {
87 // rooted path
88 path = new_cstr_char_array (unixpath);
89 tempout = new_cstr_char_array (unixpath); // paths only shrink.
90 }
91 else
92 {
93 path = new_cstr_char_array (cwd + unixpath);
94 tempout = new_cstr_char_array (cwd + unixpath); //paths only shrink.
95 }
96
97 // FIXME: handle .. depth tests to prevent / + ../foo/ stepping out
98 // of the cygwin tree
99 // FIXME: handle /./ sequences
100 bool sawslash = false;
101 char *outptr = tempout;
102 for (char *ptr=path; *ptr; ++ptr)
103 {
104 if (*ptr == '/' && sawslash)
105 --outptr;
106 else if (*ptr == '/')
107 sawslash=true;
108 else
109 sawslash=false;
110 *outptr++ = *ptr;
111 }
112 std::string rv = tempout;
113 delete[] path;
114 delete[] tempout;
115 return rv;
116 }
117
118 wchar_t *
119 io_stream_cygfile::w_str ()
120 {
121 if (!wname)
122 {
123 wname = new wchar_t [fname.size () + 7];
124 if (wname)
125 mklongpath (wname, fname.c_str (), fname.size () + 7);
126 }
127 return wname;
128 }
129
130 static void
131 get_root_dir_now ()
132 {
133 if (get_root_dir ().size())
134 return;
135 read_mounts (std::string ());
136 }
137
138 static bool
139 compactos_is_useless (const std::string& name)
140 {
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. */
150 }
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. */
154 return false;
155 }
156
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)
159 {
160 errno = 0;
161 if (!name.size())
162 {
163 Log (LOG_TIMESTAMP) << "io_stream_cygfile: Bad parameters" << endLog;
164 return;
165 }
166
167 /* do this every time because the mount points may change due to fwd/back button use...
168 * TODO: make this less...manual
169 */
170 get_root_dir_now ();
171 if (!get_root_dir ().size())
172 {
173 /* TODO: assign a errno for "no mount table :} " */
174 Log (LOG_TIMESTAMP) << "io_stream_cygfile: Error reading mounts" << endLog;
175 return;
176 }
177
178 fname = cygpath (normalise(name));
179 if (mode.size ())
180 {
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);
185 if (!fp)
186 {
187 lasterr = errno;
188 Log (LOG_TIMESTAMP) << "io_stream_cygfile: fopen(" << name << ") failed " << errno << " "
189 << strerror(errno) << endLog;
190 }
191
192 if (mode[0] == 'w' && compact_os_is_available && CompactOsOption >= 0
193 && !compactos_is_useless (name))
194 compact_os_algorithm = CompactOsOption;
195 }
196 }
197
198 io_stream_cygfile::~io_stream_cygfile ()
199 {
200 if (fp)
201 fclose (fp);
202 if (wname)
203 delete [] wname;
204 }
205
206 /* Static members */
207 int
208 io_stream_cygfile::exists (const std::string& path)
209 {
210 get_root_dir_now ();
211 if (!get_root_dir ().size())
212 return 0;
213
214 size_t len = cygpath (normalise(path)).size () + 7;
215 WCHAR wname[len];
216 mklongpath (wname, cygpath (normalise(path)).c_str (), len);
217 DWORD attr = GetFileAttributesW (wname);
218 if (attr != INVALID_FILE_ATTRIBUTES)
219 return 1;
220 return 0;
221 }
222
223 int
224 io_stream_cygfile::remove (const std::string& path)
225 {
226 if (!path.size())
227 return 1;
228 get_root_dir_now ();
229 if (!get_root_dir ().size())
230 /* TODO: assign a errno for "no mount table :} " */
231 return 1;
232
233 size_t len = cygpath (normalise(path)).size () + 7;
234 WCHAR wpath[len];
235 mklongpath (wpath, cygpath (normalise(path)).c_str (), len);
236
237 unsigned long w = GetFileAttributesW (wpath);
238 if (w != INVALID_FILE_ATTRIBUTES && w & FILE_ATTRIBUTE_DIRECTORY)
239 {
240 len = wcslen (wpath);
241 WCHAR tmp[len + 10];
242 wcscpy (tmp, wpath);
243 int i = 0;
244 do
245 {
246 ++i;
247 swprintf (tmp + len, L"old-%d", i);
248 }
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);
253 }
254 return io_stream::remove (std::string ("file://") + cygpath (normalise(path)).c_str());
255 }
256
257 /* Returns 0 for success */
258 int
259 io_stream_cygfile::mklink (const std::string& _from, const std::string& _to,
260 io_stream_link_t linktype)
261 {
262 if (!_from.size() || !_to.size())
263 return 1;
264 std::string from(normalise(_from));
265 std::string to (normalise(_to));
266 switch (linktype)
267 {
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:
273 {
274 /* First try to create a real hardlink. */
275 if (!mkcyghardlink (cygpath (from).c_str(), cygpath (to).c_str ()))
276 return 0;
277
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. */
282
283 /* textmode alert: should we translate when linking from an binmode to a
284 text mode mount and vice verca?
285 */
286 io_stream *in = io_stream::open (std::string ("cygfile://") + to, "rb", 0);
287 if (!in)
288 {
289 Log (LOG_TIMESTAMP) << "could not open " << to
290 << " for reading in mklink" << endLog;
291 return 1;
292 }
293 io_stream *out = io_stream::open (std::string ("cygfile://") + from, "wb", 0644);
294 if (!out)
295 {
296 Log (LOG_TIMESTAMP) << "could not open " << from
297 << " for writing in mklink" << endLog;
298 delete in;
299 return 1;
300 }
301
302 if (io_stream::copy (in, out))
303 {
304 Log (LOG_TIMESTAMP) << "Failed to hardlink " << from << "->"
305 << to << " during file copy." << endLog;
306 delete in;
307 delete out;
308 return 1;
309 }
310 delete in;
311 delete out;
312 return 0;
313 }
314 }
315 return 1;
316 }
317
318
319 /* virtuals */
320
321 ssize_t
322 io_stream_cygfile::read (void *buffer, size_t len)
323 {
324 ssize_t ret = 0;
325
326 if (len && fp && !feof (fp))
327 {
328 clearerr (fp);
329 size_t fret = fread (buffer, 1, len, fp);
330 if (fret < len && ferror (fp))
331 {
332 lasterr = errno;
333 ret = -1;
334 }
335 else
336 ret = (ssize_t) fret;
337 }
338 return ret;
339 }
340
341 ssize_t
342 io_stream_cygfile::write (const void *buffer, size_t len)
343 {
344 ssize_t ret = 0;
345
346 if (len && fp)
347 {
348 clearerr (fp);
349 size_t fret = fwrite (buffer, 1, len, fp);
350 if (fret < len && ferror (fp))
351 {
352 lasterr = errno;
353 ret = -1;
354 }
355 else
356 ret = (ssize_t) fret;
357 }
358 return ret;
359 }
360
361 ssize_t
362 io_stream_cygfile::peek (void *buffer, size_t len)
363 {
364 if (fp)
365 {
366 off_t pos = ftello (fp);
367 ssize_t rv = read (buffer, len);
368 fseeko (fp, pos, SEEK_SET);
369 return rv;
370 }
371 return 0;
372 }
373
374 off_t
375 io_stream_cygfile::tell ()
376 {
377 if (fp)
378 return ftello (fp);
379
380 return 0;
381 }
382
383 off_t
384 io_stream_cygfile::seek (off_t where, io_stream_seek_t whence)
385 {
386 if (fp)
387 {
388 return fseeko (fp, where, (int) whence);
389 }
390 lasterr = EBADF;
391 return -1;
392 }
393
394 int
395 io_stream_cygfile::error ()
396 {
397 if (fp)
398 return ferror (fp);
399 return lasterr;
400 }
401
402 int
403 cygmkdir_p (path_type_t isadir, const std::string& _name, mode_t mode)
404 {
405 if (!_name.size())
406 return 1;
407 std::string name(io_stream_cygfile::normalise(_name));
408 get_root_dir_now ();
409 if (!get_root_dir ().size())
410 /* TODO: assign a errno for "no mount table :} " */
411 return 1;
412 return mkdir_p (isadir == PATH_TO_DIR ? 1 : 0, cygpath (name).c_str(), mode);
413 }
414
415 int
416 io_stream_cygfile::set_mtime (time_t mtime)
417 {
418 if (!fname.size())
419 return 1;
420 if (fp)
421 fclose (fp);
422 long long ftimev = mtime * NSPERSEC + FACTOR;
423 FILETIME ftime;
424 ftime.dwHighDateTime = ftimev >> 32;
425 ftime.dwLowDateTime = ftimev;
426 HANDLE h;
427 h = CreateFileW (w_str (), GENERIC_WRITE,
428 FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING,
429 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, 0);
430 if (h == INVALID_HANDLE_VALUE)
431 return 1;
432
433 if (compact_os_algorithm >= 0)
434 {
435 /* Compact OS must be applied after last WriteFile()
436 and before SetFileTime(). */
437 int rc = CompactOsCompressFile (h, compact_os_algorithm);
438 if (rc < 0)
439 {
440 DWORD err = GetLastError();
441 Log (LOG_TIMESTAMP) << "Compact OS disabled after error " << err
442 << " on " << fname << endLog;
443 compact_os_is_available = false;
444 }
445 else
446 Log (LOG_BABBLE) << "Compact OS algorithm " << compact_os_algorithm
447 << (rc == 0 ? " not" : "") << " applied to " << fname << endLog;
448 }
449
450 SetFileTime (h, 0, 0, &ftime);
451 CloseHandle (h);
452 return 0;
453 }
454
455 int
456 io_stream_cygfile::move (const std::string& _from, const std::string& _to)
457 {
458 if (!_from.size() || !_to.size())
459 return 1;
460 std::string from (normalise(_from));
461 std::string to(normalise(_to));
462 get_root_dir_now ();
463 if (!get_root_dir ().size())
464 /* TODO: assign a errno for "no mount table :} " */
465 return 1;
466 return rename (cygpath (from).c_str(), cygpath (to).c_str());
467 }
468
469 size_t
470 io_stream_cygfile::get_size ()
471 {
472 if (!fname.size() )
473 return 0;
474 HANDLE h;
475 DWORD ret = 0;
476 h = CreateFileW (w_str (), GENERIC_READ,
477 FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING,
478 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, 0);
479 if (h != INVALID_HANDLE_VALUE)
480 {
481 ret = GetFileSize (h, NULL);
482 CloseHandle (h);
483 }
484 return ret;
485 }
This page took 0.108625 seconds and 6 git commands to generate.