a580e12000-09-27Fredrik Hübinette (Hubbe) #pike __REAL_VERSION__
a20af62000-09-26Fredrik Hübinette (Hubbe) 
afb5002013-07-09Henrik Grubbström (Grubba) //! Filesystem which can be used to mount a Tar file. //! //! Two kinds of extended tar file records are supported: //! @string
d441622013-07-09Henrik Grubbström (Grubba) //! @value "ustar\0\60\60"
afb5002013-07-09Henrik Grubbström (Grubba) //! POSIX ustar (Version 0?). //! @value "ustar \0" //! GNU tar (POSIX draft) //! @endstring //! //! @note //! For a quick start, you probably want to use @[`()()]. //! //! @seealso //! @[`()()]
278ec62009-02-12Martin Stjernholm constant EXTRACT_SKIP_MODE = 1;
afb5002013-07-09Henrik Grubbström (Grubba) //! Don't set any permission bits from the tar records.
6e15d92009-02-13Martin Stjernholm constant EXTRACT_SKIP_EXT_MODE = 2;
afb5002013-07-09Henrik Grubbström (Grubba) //! Don't set set-user-ID, set-group-ID, or sticky bits from //! the tar records.
6e15d92009-02-13Martin Stjernholm constant EXTRACT_SKIP_MTIME = 4;
afb5002013-07-09Henrik Grubbström (Grubba) //! Don't set mtime from the tar records.
6e15d92009-02-13Martin Stjernholm constant EXTRACT_CHOWN = 8;
afb5002013-07-09Henrik Grubbström (Grubba) //! Set owning user and group from the tar records.
278ec62009-02-12Martin Stjernholm 
afb5002013-07-09Henrik Grubbström (Grubba) constant EXTRACT_ERR_ON_UNKNOWN = 16; //! Throw an error if an entry of an unsupported type is //! encountered. This is ignored otherwise.
7219522003-12-17Martin Nilsson 
d86d8c2010-10-19Henrik Grubbström (Grubba) //! Low-level Tar Filesystem. class _Tar
e2439f1999-01-31Mirar (Pontus Hagland) {
d396c82009-02-13Martin Stjernholm  object fd;
e6ddaa1999-12-26Johan Sundström  string filename;
1910272009-02-12Martin Stjernholm  protected int fd_in_use = -1; // If >= 0 then some ReadFile is in use. The value is the start // position of that file.
a1a3392009-02-12Martin Stjernholm 
e6ddaa1999-12-26Johan Sundström  class ReadFile {
a1a3392009-02-12Martin Stjernholm  inherit Stdio.BlockFile;
e6ddaa1999-12-26Johan Sundström 
a1a3392009-02-12Martin Stjernholm  protected private int start, len;
e6ddaa1999-12-26Johan Sundström 
1910272009-02-12Martin Stjernholm  protected int cached_pos, cached_errno; // Used only when the fd has been released.
9eaf1d2008-06-28Martin Nilsson  protected string _sprintf(int t)
dc5f092001-08-23Henrik Grubbström (Grubba)  {
a1a3392009-02-12Martin Stjernholm  return t=='O' && sprintf("Filesystem.Tar.ReadFile(%d, %d)", start, len);
dc5f092001-08-23Henrik Grubbström (Grubba)  }
1910272009-02-12Martin Stjernholm  protected void allocate_fd() { if (fd_in_use >= 0 && fd_in_use != start) error ("Concurrent file use in %O.\n", _Tar::this); fd_in_use = start; } protected void release_fd()
e6ddaa1999-12-26Johan Sundström  {
1910272009-02-12Martin Stjernholm  if (fd_in_use == start) { if (fd->tell) cached_pos = fd->tell(); if (fd->errno) cached_errno = fd->errno(); fd_in_use = -1; } }
a1a3392009-02-12Martin Stjernholm 
1910272009-02-12Martin Stjernholm  protected void create(int p, int l) {
e6ddaa1999-12-26Johan Sundström  start = p; len = l;
1910272009-02-12Martin Stjernholm  allocate_fd();
63fbda2009-01-28Martin Stjernholm  if (fd->seek(start) < 0) error ("Failed to seek to position %d in %O.\n", start, fd);
a1a3392009-02-12Martin Stjernholm  }
c071bc2017-11-05Henrik Grubbström (Grubba)  protected void _destruct()
a1a3392009-02-12Martin Stjernholm  {
1910272009-02-12Martin Stjernholm  fd_in_use = -1;
a1a3392009-02-12Martin Stjernholm  } string read (void|int read_len) { if (start < 0) error ("File not open.\n");
1910272009-02-12Martin Stjernholm  allocate_fd();
a1a3392009-02-12Martin Stjernholm  int max_len = len - (fd->tell() - start);
1910272009-02-12Martin Stjernholm  string res = fd->read (read_len ? min (read_len, max_len) : max_len); if (sizeof (res) == max_len) // For compatibility: If we read to the end then release the // fd for use from another ReadFile, so that it works if this // one isn't properly closed or destructed first. release_fd(); return res;
a1a3392009-02-12Martin Stjernholm  } void close() {
1910272009-02-12Martin Stjernholm  release_fd();
a1a3392009-02-12Martin Stjernholm  start = -1; } int seek (int to) { if (start < 0) error ("File not open.\n");
1910272009-02-12Martin Stjernholm  allocate_fd();
a1a3392009-02-12Martin Stjernholm  if (to < 0) to += len; if (to < 0 || to > len) return -1; return fd->seek (start + to); } int tell() { if (start < 0) error ("File not open.\n");
1910272009-02-12Martin Stjernholm  if (fd_in_use != start) return cached_pos - start; else return fd->tell() - start;
a1a3392009-02-12Martin Stjernholm  } int errno() {
1910272009-02-12Martin Stjernholm  if (start < 0 || fd_in_use != start) return cached_errno; else return fd->errno();
e6ddaa1999-12-26Johan Sundström  } } class Record { inherit Filesystem.Stat; constant RECORDSIZE = 512; constant NAMSIZ = 100; constant TUNMLEN = 32; constant TGNMLEN = 32; constant SPARSE_EXT_HDR = 21; constant SPARSE_IN_HDR = 4; string arch_name; int linkflag; string arch_linkname; string magic; int devmajor; int devminor; int chksum; int pos; int pseudo;
40da092002-03-13Henrik Grubbström (Grubba)  // Header description: // // Fieldno Offset len Description
3524712015-05-26Martin Nilsson  //
40da092002-03-13Henrik Grubbström (Grubba)  // 0 0 100 Filename // 1 100 8 Mode (octal) // 2 108 8 uid (octal) // 3 116 8 gid (octal) // 4 124 12 size (octal) // 5 136 12 mtime (octal) // 6 148 8 chksum (octal) // 7 156 1 linkflag // 8 157 100 linkname // 9 257 8 magic // 10 265 32 (USTAR) uname // 11 297 32 (USTAR) gname // 12 329 8 devmajor (octal) // 13 337 8 devminor (octal) // 14 345 167 (USTAR) Long path // // magic can be any of: // "ustar\0""00" POSIX ustar (Version 0?). // "ustar \0" GNU tar (POSIX draft)
e6ddaa1999-12-26Johan Sundström  void create(void|string s, void|int _pos) { if(!s)
e2439f1999-01-31Mirar (Pontus Hagland)  {
e6ddaa1999-12-26Johan Sundström  pseudo = 1; return;
e2439f1999-01-31Mirar (Pontus Hagland)  }
ab87901999-06-09Marcus Comstedt 
e6ddaa1999-12-26Johan Sundström  pos = _pos; array a = array_sscanf(s, "%"+((string)NAMSIZ)+"s%8s%8s%8s%12s%12s%8s" "%c%"+((string)NAMSIZ)+"s%8s" "%"+((string)TUNMLEN)+"s"
40da092002-03-13Henrik Grubbström (Grubba)  "%"+((string)TGNMLEN)+"s%8s%8s%167s");
e6ddaa1999-12-26Johan Sundström  sscanf(a[0], "%s%*[\0]", arch_name); sscanf(a[1], "%o", mode); sscanf(a[2], "%o", uid); sscanf(a[3], "%o", gid); sscanf(a[4], "%o", size); sscanf(a[5], "%o", mtime); sscanf(a[6], "%o", chksum); linkflag = a[7]; sscanf(a[8], "%s%*[\0]", arch_linkname); sscanf(a[9], "%s%*[\0]", magic);
40da092002-03-13Henrik Grubbström (Grubba)  if((magic=="ustar ") || (magic == "ustar"))
ab87901999-06-09Marcus Comstedt  {
40da092002-03-13Henrik Grubbström (Grubba)  // GNU ustar or POSIX ustar
e6ddaa1999-12-26Johan Sundström  sscanf(a[10], "%s\0", uname); sscanf(a[11], "%s\0", gname);
40da092002-03-13Henrik Grubbström (Grubba)  if (a[9] == "ustar\0""00") { // POSIX ustar (Version 0?) string long_path = ""; sscanf(a[14], "%s\0", long_path); if (sizeof(long_path)) { arch_name = long_path + "/" + arch_name; } } else if (arch_name == "././@LongLink") { // GNU tar
2501132003-09-08Martin Nilsson  // Data contains full filename of next record. pseudo = 2;
40da092002-03-13Henrik Grubbström (Grubba)  }
ab87901999-06-09Marcus Comstedt  }
e6ddaa1999-12-26Johan Sundström  else uname = gname = 0; sscanf(a[12], "%o", devmajor); sscanf(a[13], "%o", devminor);
1470ab2002-11-24Marcus Comstedt  fullpath = combine_path_unix("/", arch_name);
e6ddaa1999-12-26Johan Sundström  name = (fullpath/"/")[-1]; atime = ctime = mtime; set_type( ([ 0:"reg", '0':"reg", '1':0, // hard link '2':"lnk", '3':"chr", '4':"blk", '5':"dir", '6':"fifo", '7':0 // contigous ])[linkflag] || "reg" ); }
d396c82009-02-13Martin Stjernholm  object open(string mode) { if(mode!="r") error("Can only read right now.\n"); return ReadFile(pos, size); } };
6e15d92009-02-13Martin Stjernholm 
e6ddaa1999-12-26Johan Sundström  array(Record) entries = ({}); array filenames; mapping filename_to_entry; void mkdirnode(string what, Record from, object parent) { Record r = Record(); if(what=="") what = "/"; r->fullpath = what; r->name = (what/"/")[-1]; r->mode = 0755|((from->mode&020)?020:0)|((from->mode&02)?02:0); r->set_type("dir"); r->uid = 0; r->gid = 0; r->size = 0; r->atime = r->ctime = r->mtime = from->mtime; r->filesystem = parent; filename_to_entry[what] = r; }
d396c82009-02-13Martin Stjernholm  void create(object fd, string filename, object parent)
e6ddaa1999-12-26Johan Sundström  {
8e06a32014-09-30Martin Nilsson  this::filename = filename;
e6ddaa1999-12-26Johan Sundström  // read all entries
8e06a32014-09-30Martin Nilsson  this::fd = fd;
e6ddaa1999-12-26Johan Sundström  int pos = 0; // fd is at position 0 here
2501132003-09-08Martin Nilsson  string next_name;
e6ddaa1999-12-26Johan Sundström  for(;;) { Record r;
8e06a32014-09-30Martin Nilsson  string s = this::fd->read(512);
e6ddaa1999-12-26Johan Sundström 
ead9722003-01-20Martin Nilsson  if(s=="" || sizeof(s)<512 || sscanf(s, "%*[\0]%*2s")==1)
e6ddaa1999-12-26Johan Sundström  break; r = Record(s, pos+512); r->filesystem = parent;
2501132003-09-08Martin Nilsson  if(r->arch_name!="" && !r->pseudo) { // valid file? if(next_name) { r->fullpath = next_name; r->name = (next_name/"/")[-1];
9340c62003-09-09Martin Nilsson  next_name = 0;
2501132003-09-08Martin Nilsson  }
e6ddaa1999-12-26Johan Sundström  entries += ({ r });
2501132003-09-08Martin Nilsson  } if(r->pseudo==2)
d396c82009-02-13Martin Stjernholm  next_name = combine_path("/", r->open("r")->read(r->size-1));
e6ddaa1999-12-26Johan Sundström  pos += 512 + r->size; if(pos%512) pos += 512 - (pos%512);
8e06a32014-09-30Martin Nilsson  if (this::fd->seek(pos) < 0)
63fbda2009-01-28Martin Stjernholm  error ("Failed to seek to position %d in %O.\n", pos, fd);
e6ddaa1999-12-26Johan Sundström  }
fae1c02016-12-10Henrik Grubbström (Grubba)  filename_to_entry = mkmapping(filenames = entries->fullpath, entries);
e6ddaa1999-12-26Johan Sundström  // create missing dirnodes array last = ({}); foreach(entries, Record r) { array path = r->fullpath/"/";
8a531a2006-11-04Martin Nilsson  if(path[..<1]==last) continue; // same dir last = path[..<1];
e6ddaa1999-12-26Johan Sundström 
fae1c02016-12-10Henrik Grubbström (Grubba)  for(int i = 0; i<sizeof(last); i++) { if(!filename_to_entry[last[..i]*"/"]) { string dir = last[..i]*"/"; mkdirnode(dir, r, parent); filenames += ({ dir }); } }
e6ddaa1999-12-26Johan Sundström  }
fae1c02016-12-10Henrik Grubbström (Grubba)  // NB: It's legal to have several entries for the same path; // the last one wins. filenames = Array.uniq(filenames);
e6ddaa1999-12-26Johan Sundström  }
0d654f2001-01-20Johan Sundström 
464d0d2015-02-26Henrik Grubbström (Grubba)  protected void extract_bits (string dest, Stdio.Stat r, int which_bits)
278ec62009-02-12Martin Stjernholm  {
464d0d2015-02-26Henrik Grubbström (Grubba)  if (!r->RECORDSIZE) { // This is just a Stdio.Stat and not a Record. // // Just restore the mode bits. which_bits = ~(EXTRACT_SKIP_MODE | EXTRACT_SKIP_EXT_MODE); }
3178bf2012-02-15Henrik Grubbström (Grubba) #if constant (utime)
f858312012-03-15Henrik Grubbström (Grubba)  if (!(which_bits & EXTRACT_SKIP_MTIME)) { mixed err = catch { utime (dest, r->mtime, r->mtime, 1); }; if (err) { #ifdef __NT__ // utime() is currently not supported for directories on WIN32.
9ae5932012-09-26Bill Welliver  if (!r->isdir)
f858312012-03-15Henrik Grubbström (Grubba) #endif werror("Tar: Failed to set modification time for %O.\n", dest); } }
3178bf2012-02-15Henrik Grubbström (Grubba) #endif
9ae5932012-09-26Bill Welliver  if (!(which_bits & EXTRACT_SKIP_MODE) && !r->islnk) {
3178bf2012-02-15Henrik Grubbström (Grubba)  if (which_bits & EXTRACT_SKIP_EXT_MODE) chmod (dest, r->mode & 0777); else chmod (dest, r->mode & 07777); }
278ec62009-02-12Martin Stjernholm #if constant (chown) if (which_bits & EXTRACT_CHOWN) { int uid;
4fc6322009-02-21Martin Stjernholm  if (array pwent = r->uname && getpwnam (r->uname))
278ec62009-02-12Martin Stjernholm  uid = pwent[2];
4fc6322009-02-21Martin Stjernholm  else uid = r->uid;
278ec62009-02-12Martin Stjernholm  int gid;
4fc6322009-02-21Martin Stjernholm  if (array grent = r->gname && getgrnam (r->gname))
278ec62009-02-12Martin Stjernholm  gid = grent[2];
4fc6322009-02-21Martin Stjernholm  else gid = r->gid;
278ec62009-02-12Martin Stjernholm 
4fc6322009-02-21Martin Stjernholm  chown (dest, uid, gid, 1);
278ec62009-02-12Martin Stjernholm  } #endif }
464d0d2015-02-26Henrik Grubbström (Grubba) #if !constant(access) // Probably NT or similar single-user OS, // so pretend we can access anything. protected int access(string path, string|void flags) { return 1; } #endif
278ec62009-02-12Martin Stjernholm  void extract (string src_dir, string dest_dir, void|string|function(string,Filesystem.Stat:int|string) filter, void|int flags) //! Extracts files from the tar file in sequential order. //! //! @param src_dir //! The root directory in the tar file system to extract. //! //! @param dest_dir //! The root directory in the real file system that will receive //! the contents of @[src_dir]. It is assumed to exist and be //! writable. //! //! @param filter //! A filter for the entries under @[src_dir] to extract. If it's //! a string then it's taken as a glob pattern which is matched //! against the path below @[src_dir]. That path always begins //! with a @expr{/@}. For directory entries it ends with a //! @expr{/@} too, otherwise not. //! //! If it's a function then it's called for every entry under //! @[src_dir], and those where it returns nonzero are extracted. //! The function receives the path part below @[src_dir] as the //! first argument, which is the same as in the glob case above, //! and the stat struct as the second. If the function returns a //! string, it's taken as the path below @[dest_dir] where this //! entry should be extracted (any missing directories are created //! automatically). //! //! If @[filter] is zero, then everything below @[src_dir] is //! extracted. //! //! @param flags //! Bitfield of flags to control the extraction: //! @int //! @value Filesystem.Tar.EXTRACT_SKIP_MODE
6e15d92009-02-13Martin Stjernholm  //! Don't set any permission bits from the tar records. //! @value Filesystem.Tar.EXTRACT_SKIP_EXT_MODE //! Don't set set-user-ID, set-group-ID, or sticky bits from //! the tar records.
278ec62009-02-12Martin Stjernholm  //! @value Filesystem.Tar.EXTRACT_SKIP_MTIME
6e15d92009-02-13Martin Stjernholm  //! Don't set mtime from the tar records.
278ec62009-02-12Martin Stjernholm  //! @value Filesystem.Tar.EXTRACT_CHOWN
6e15d92009-02-13Martin Stjernholm  //! Set owning user and group from the tar records.
278ec62009-02-12Martin Stjernholm  //! @value Filesystem.Tar.EXTRACT_ERR_ON_UNKNOWN //! Throw an error if an entry of an unsupported type is //! encountered. This is ignored otherwise. //! @endint //! //! Files and directories are supported on all platforms, and //! symlinks are supported whereever @[symlink] exists. Other record //! types are currently not supported. //! //! @throws //! I/O errors are thrown. { if (!has_suffix (src_dir, "/")) src_dir += "/"; if (has_suffix (dest_dir, "/")) dest_dir = dest_dir[..<1];
6e15d92009-02-13Martin Stjernholm  int do_extract_bits = (flags & (EXTRACT_SKIP_MODE|EXTRACT_SKIP_MTIME|EXTRACT_CHOWN)) != (EXTRACT_SKIP_MODE|EXTRACT_SKIP_MTIME);
464d0d2015-02-26Henrik Grubbström (Grubba)  mapping(string:Stdio.Stat) restore_bits = ([]);
278ec62009-02-12Martin Stjernholm 
464d0d2015-02-26Henrik Grubbström (Grubba)  // Similar to Stdio.mkdirhier(), but ensures that we have // read, write and execute permission on the resulting directory. // // The original directory permissions (if altered) // are registered in the variable "restore_bits" above. int mkdirhier(string pathname) { string path = ""; #ifdef __NT__ pathname = replace(pathname, "\\", "/"); if (pathname[1..2] == ":/" && `<=("A", upper_case(pathname[..0]), "Z")) path = pathname[..2], pathname = pathname[3..]; #endif if (pathname == "") pathname = "."; array(string) segments = pathname/"/"; if (segments[0] == "") { path += "/"; pathname = pathname[1..]; segments = segments[1..]; } // FIXME: An alternative could be a binary search, // but since it is usually only the last few // segments of the path that are missing, we // just do a linear search from the end. int i = sizeof(segments); while (i--) { string p = path + segments[..i]*"/"; Stdio.Stat st; if ((st = file_stat(p))) { if (!st->isdir) return 0; if (!access(p, "rwx")) { // We're not allowed to write. // Attempt to adjust the permissions temporarily. catch { chmod(p, st->mode | 0700); if (!restore_bits[p]) { // Register p for permission restoration. restore_bits[p] = st; } };
278ec62009-02-12Martin Stjernholm  }
464d0d2015-02-26Henrik Grubbström (Grubba)  break; } } if (!i && !sizeof(path) && !access(".", "rwx")) { // We're not allowed to write. // Attempt to adjust the permissions temporarily. catch { Stdio.Stat st = file_stat("."); chmod(".", st->mode | 0700); if (!restore_bits["."]) { // Register "." for permission restoration. restore_bits["."] = st; } }; } i++; while (i < sizeof(segments)) { string p = path + segments[..i++] * "/"; if (!mkdir(p, 0777)) { if (errno() != System.EEXIST) return 0;
278ec62009-02-12Martin Stjernholm  else {
464d0d2015-02-26Henrik Grubbström (Grubba)  Stdio.Stat st; if ((st = file_stat(p))) { if (!st->isdir) return 0; // Directory exists, but we couldn't access it // in the earlier loop, so it was probably hidden. if (!access(path, "rw")) { // We're not allowed to write. // Attempt to adjust the permissions temporarily. catch { chmod(p, st->mode | 0700); if (!restore_bits[p]) { // Register p for permission restoration. restore_bits[p] = st; } }; }
278ec62009-02-12Martin Stjernholm  }
464d0d2015-02-26Henrik Grubbström (Grubba)  } } } return Stdio.is_dir(path + pathname) && access(path + pathname, "rwx"); }; mixed err = catch { foreach (entries, Record r) { string fullpath = r->fullpath; if (has_prefix (fullpath, src_dir)) { string subpath = fullpath[sizeof (src_dir) - 1..]; string|int filter_res; if (!filter || (stringp (filter) ? glob (filter, subpath) : (filter_res = filter (subpath, r)))) { string destpath = dest_dir + (stringp (filter_res) ? filter_res : subpath); if (r->isdir) { if (!mkdirhier(destpath))
8490d22016-11-05Martin Nilsson  error ("Failed to create directory %q: %s.\n", destpath, strerror(errno()));
464d0d2015-02-26Henrik Grubbström (Grubba)  // Set bits etc afterwards on dirs. if (do_extract_bits) restore_bits[destpath] = r; } else { string dest_dir = (destpath / "/")[..<1] * "/"; if (!mkdirhier(dest_dir))
8490d22016-11-05Martin Nilsson  error ("Failed to create directory %q: %s.\n", destpath, strerror(errno()));
464d0d2015-02-26Henrik Grubbström (Grubba)  if (r->isreg) { Stdio.File o = Stdio.File(); if (!o->open (destpath, "wct")) { // Could be because the file exists, but // with bad access permissions for us. Stdio.Stat st = file_stat(destpath); if (!st || catch { chmod(destpath, st->mode | 0600); if (!do_extract_bits) restore_bits[destpath] = st; } || !o->open (destpath, "wct")) {
51f1ce2015-09-06Martin Nilsson  error ("Failed to create %q: %s.\n",
464d0d2015-02-26Henrik Grubbström (Grubba)  destpath, strerror (o->errno())); } } Stdio.BlockFile i = r->open ("r"); do { string data = i->read (1024 * 1024); if (data == "") break; if (o->write (data) != sizeof (data))
51f1ce2015-09-06Martin Nilsson  error ("Failed to write %q: %s.\n",
464d0d2015-02-26Henrik Grubbström (Grubba)  destpath, strerror (o->errno())); } while (1); i->close(); o->close(); if (do_extract_bits) extract_bits (destpath, r, flags); }
278ec62009-02-12Martin Stjernholm  #if constant (symlink)
464d0d2015-02-26Henrik Grubbström (Grubba)  else if (r->islnk) { symlink (r->arch_linkname, destpath);
278ec62009-02-12Martin Stjernholm 
464d0d2015-02-26Henrik Grubbström (Grubba)  if (do_extract_bits) extract_bits (destpath, r, flags); }
278ec62009-02-12Martin Stjernholm #endif
464d0d2015-02-26Henrik Grubbström (Grubba)  else if (flags & EXTRACT_ERR_ON_UNKNOWN) error ("Failed to extract entry of unsupported type %x: %O\n", r->mode & 0xf000, r); } }
278ec62009-02-12Martin Stjernholm  } }
464d0d2015-02-26Henrik Grubbström (Grubba)  }; // Make sure to restore directory permissions, etc // even if we've failed. if (sizeof(restore_bits)) { array(string) paths = indices(restore_bits); foreach (reverse(sort(paths)), string destpath) { mixed e = catch { extract_bits(destpath, restore_bits[destpath], flags); }; if (!err) err = e;
278ec62009-02-12Martin Stjernholm  } }
464d0d2015-02-26Henrik Grubbström (Grubba)  if (err) throw(err);
278ec62009-02-12Martin Stjernholm  }
afb5002013-07-09Henrik Grubbström (Grubba)  protected string _sprintf(int t)
0d654f2001-01-20Johan Sundström  {
1a38432002-11-29Martin Nilsson  return t=='O' && sprintf("_Tar(/* filename=%O */)", filename);
0d654f2001-01-20Johan Sundström  }
5df1992003-09-23Martin Nilsson }
e2439f1999-01-31Mirar (Pontus Hagland) 
afb5002013-07-09Henrik Grubbström (Grubba) //!
e2439f1999-01-31Mirar (Pontus Hagland) class _TarFS {
e6ddaa1999-12-26Johan Sundström  inherit Filesystem.System; _Tar tar;
afb5002013-07-09Henrik Grubbström (Grubba)  //! protected void create(_Tar _tar, string _wd, string _root, Filesystem.Base _parent)
e6ddaa1999-12-26Johan Sundström  { tar = _tar; sscanf(reverse(_wd), "%*[\\/]%s", wd); wd = reverse(wd); if(wd=="") wd = "/"; sscanf(_root, "%*[/]%s", root); parent = _parent; }
afb5002013-07-09Henrik Grubbström (Grubba)  protected string _sprintf(int t)
0d654f2001-01-20Johan Sundström  {
1a38432002-11-29Martin Nilsson  return t=='O' && sprintf("_TarFS(/* root=%O, wd=%O */)", root, wd);
0d654f2001-01-20Johan Sundström  }
dcae092000-09-18Henrik Grubbström (Grubba)  Filesystem.Stat stat(string file, void|int lstat)
e6ddaa1999-12-26Johan Sundström  {
1e599a2001-06-19Marcus Wellhardh  file = combine_path_unix(wd, file);
e6ddaa1999-12-26Johan Sundström  return tar->filename_to_entry[root+file]; }
01d5b62016-11-29Henrik Grubbström (Grubba)  protected string fixup_dir(string path, string dir) { path = path[sizeof(dir)..]; return (path/"/")[0]; }
e6ddaa1999-12-26Johan Sundström  array(string) get_dir(void|string directory, void|string|array globs) {
1e599a2001-06-19Marcus Wellhardh  directory = combine_path_unix(wd, (directory||""), "");
e6ddaa1999-12-26Johan Sundström 
01d5b62016-11-29Henrik Grubbström (Grubba)  if (!has_suffix(directory, "/")) directory += "/"; array(string) f = map(filter(tar->filenames, has_prefix, directory), fixup_dir, directory); if (globs) { f = glob(globs, f); }
e6ddaa1999-12-26Johan Sundström 
01d5b62016-11-29Henrik Grubbström (Grubba)  return Array.uniq(f - ({ "" }));
e6ddaa1999-12-26Johan Sundström  } Filesystem.Base cd(string directory) { Filesystem.Stat st = stat(directory); if(!st) return 0;
9ae5932012-09-26Bill Welliver  if(st->isdir) // stay in this filesystem
e6ddaa1999-12-26Johan Sundström  { object new = _TarFS(tar, st->fullpath, root, parent); return new; } return st->cd(); // try something else } Stdio.File open(string filename, string mode) {
1e599a2001-06-19Marcus Wellhardh  filename = combine_path_unix(wd, filename);
e6ddaa1999-12-26Johan Sundström  return tar->filename_to_entry[root+filename] &&
d396c82009-02-13Martin Stjernholm  tar->filename_to_entry[root+filename]->open(mode);
e6ddaa1999-12-26Johan Sundström  } int access(string filename, string mode) { return 1; // sure }
afb5002013-07-09Henrik Grubbström (Grubba)  //! @fixme //! Not implemented yet.
e6ddaa1999-12-26Johan Sundström  int rm(string filename) { }
afb5002013-07-09Henrik Grubbström (Grubba)  //! @fixme //! Not implemented yet.
e6ddaa1999-12-26Johan Sundström  void chmod(string filename, int|string mode) { }
afb5002013-07-09Henrik Grubbström (Grubba)  //! @fixme //! Not implemented yet.
e6ddaa1999-12-26Johan Sundström  void chown(string filename, int|object owner, int|object group) { }
e2439f1999-01-31Mirar (Pontus Hagland) } class `() {
e6ddaa1999-12-26Johan Sundström  inherit _TarFS;
e2439f1999-01-31Mirar (Pontus Hagland) 
afb5002013-07-09Henrik Grubbström (Grubba)  //! @decl void create(string filename, void|Filesystem.Base parent,@ //! void|object file) //! //! @param filename //! The tar file to mount. //! //! @param parent //! The parent filesystem. If none is given, the normal system //! filesystem is assumed. This allows mounting a TAR-file within //! a tarfile. //! //! @param file //! If specified, this should be an open file descriptor. This object //! could e.g. be a @[Stdio.File], @[Gz.File] or @[Bz2.File] object. protected void create(string|object filename, void|Filesystem.Base parent, void|object file)
e6ddaa1999-12-26Johan Sundström  { if(!parent) parent = Filesystem.System();
e2439f1999-01-31Mirar (Pontus Hagland) 
afb5002013-07-09Henrik Grubbström (Grubba)  if(!file) file = parent->open(filename, "r");
b84fc32003-12-16H. William Welliver III 
afb5002013-07-09Henrik Grubbström (Grubba)  if(!file)
e6ddaa1999-12-26Johan Sundström  error("Not a Tar file\n");
e2439f1999-01-31Mirar (Pontus Hagland) 
afb5002013-07-09Henrik Grubbström (Grubba)  _Tar tar = _Tar(file, filename, this);
e2439f1999-01-31Mirar (Pontus Hagland) 
e6ddaa1999-12-26Johan Sundström  _TarFS::create(tar, "/", "", parent); }
0d654f2001-01-20Johan Sundström 
1a38432002-11-29Martin Nilsson  string _sprintf(int t)
0d654f2001-01-20Johan Sundström  {
1a38432002-11-29Martin Nilsson  return t=='O' && sprintf("Filesystem.Tar(/* tar->filename=%O, root=%O, wd=%O */)", tar && tar->filename, root, wd);
0d654f2001-01-20Johan Sundström  }
e2439f1999-01-31Mirar (Pontus Hagland) }