1 //============================================================================
2 // Name : acngfs.cpp
3 // Author : Eduard Bloch
4 // Description : Simple FUSE-based filesystem for HTTP access (apt-cacher NG)
5 //============================================================================
6
7
8 #define LOCAL_DEBUG
9 #include "debug.h"
10
11 #include "acsyscap.h"
12
13 #include "meta.h"
14 #include "header.h"
15 #include "caddrinfo.h"
16 #include "sockio.h"
17 #include "acbuf.h"
18 #include "acfg.h"
19 #include "lockable.h"
20 #include "cleaner.h"
21 #include "tcpconnect.h"
22
23 #include "fileitem.h"
24 #include "dlcon.h"
25
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #ifdef HAVE_SYS_MOUNT_H
29 #include <sys/param.h>
30 #include <sys/mount.h>
31 #endif
32 #ifdef HAVE_SYS_VFS_H
33 #include <sys/vfs.h>
34 #endif
35
36 #include <unistd.h>
37 #include <inttypes.h>
38 #include <stdint.h>
39 #include <pthread.h>
40 #include <errno.h>
41 #include <signal.h>
42
43 #include <cstdio>
44 #include <algorithm>
45 #include <iostream>
46 #include <list>
47
48
49 #define FUSE_USE_VERSION 25
50 #include <fuse.h>
51
52 #ifdef HAVE_DLOPEN
53 #include <dlfcn.h>
54 #endif
55
56 #define HEADSZ 5000
57 #ifndef MIN
58 #define MIN(a,b) ( (a<=b)?a:b)
59 #endif
60
61 using namespace std;
62 using namespace acng;
63
64 // needing some local definitions to make the linker happy w/o the fat source
65 namespace acng {
66 cmstring sDefPortHTTP("3142"), sDefPortHTTPS("80");
67 cmstring sEmptyString;
68 }
69
70 #ifdef SPAM
71 #define _cerr(x) cerr << x
72 #warning printing spam all around
73 #else
74 #define _cerr(x)
75 #endif
76
77 #define POOLMAXSIZE 20 // max size
78 #define POOLMAXAGE 50 // seconds
79
80 // some globals, set only once
81 static struct stat statTempl;
82 static struct statfs stfsTemp;
83 static tHttpUrl baseUrl, proxyUrl;
84 static mstring altPath;
85 bool g_bGoodServer=true;
86
87 struct tDlDesc
88 {
89 cmstring m_path;
90 uint m_ftype;
91
92 virtual int Read(char *retbuf, const char *path, off_t pos, size_t len) =0;
93 virtual int Stat(struct stat &stbuf) =0;
tDlDesctDlDesc94 tDlDesc(cmstring &p, uint ftype) : m_path(p), m_ftype(ftype) {};
~tDlDesctDlDesc95 virtual ~tDlDesc() {};
96 };
97
98 struct tDlDescLocal : public tDlDesc
99 {
100 FILE *pFile;
tDlDescLocaltDlDescLocal101 tDlDescLocal(cmstring &path, uint ftype) : tDlDesc(path, ftype), pFile(nullptr)
102 {
103 };
104
StattDlDescLocal105 int Stat(struct stat &stbuf)
106 {
107 if(altPath.empty()) // hm?
108 return -ENOENT;
109
110 if (::stat((altPath + m_path).c_str(), &stbuf))
111 return -errno;
112
113 // verify the file state
114 header h;
115 int r = h.LoadFromFile(altPath + m_path + ".head");
116 if (r <= 0 || stbuf.st_size != atoofft(h.h[header::CONTENT_LENGTH], -23))
117 return -EIO;
118
119 return 0;
120 }
121
~tDlDescLocaltDlDescLocal122 virtual ~tDlDescLocal()
123 {
124 if(pFile)
125 fclose(pFile);
126 pFile=nullptr;
127 };
128
ReadtDlDescLocal129 int Read(char *retbuf, const char *path, off_t pos, size_t len)
130 {
131 if (!pFile)
132 {
133 struct stat stbuf;
134 if(Stat(stbuf))
135 return -EIO; // file incomplete or missing
136
137 FILE *pf = fopen((altPath + m_path).c_str(), "rb");
138 if (!pf)
139 return -EIO;
140 pFile = pf;
141 }
142
143 int copied=0;
144 if(pFile && 0==fseeko(pFile, pos, SEEK_SET))
145 {
146 while(!feof(pFile) && !ferror(pFile) && len>0)
147 {
148 size_t r = ::fread(retbuf+copied, 1, len, pFile);
149 copied+=r;
150 len-=r;
151 }
152 }
153 return ferror(pFile) ? -EIO : copied;
154 }
155 };
156
157 struct tFileId
158 { off_t m_size; mstring m_ctime;
tFileIdtFileId159 tFileId() : m_size(0) {};
tFileIdtFileId160 tFileId(off_t a, mstring b) : m_size(a), m_ctime(b) {};
operator !=tFileId161 bool operator!=(tFileId other) const { return m_size != other.m_size || m_ctime != other.m_ctime;}
162 };
163 static class : public base_with_mutex, public map<string, tFileId>
164 {} remote_info_cache;
165
166 struct tDlDescRemote : public tDlDesc
167 {
168 protected:
169
170 tFileId fid;
171 bool bIsFirst; // hint to catch the validation data when download starts
172
173 public:
tDlDescRemotetDlDescRemote174 tDlDescRemote(cmstring &p, uint n) : tDlDesc(p,n), bIsFirst(true)
175 {
176 // expire the caches every time, should not cost much anyway
177 g_tcp_con_factory.BackgroundCleanup();
178 };
179
ReadtDlDescRemote180 int Read(char *retbuf, const char *path, off_t pos, size_t len)
181 {
182 dlcon dler(true, 0);
183 tHttpUrl uri = proxyUrl;
184 uri.sPath += baseUrl.sHost
185 // + ":" + ( baseUrl.sPort.empty() ? baseUrl.sPort : "80")
186 + baseUrl.sPath + m_path;
187 class tFitem: public fileitem
188 {
189 public:
190 char *pRet;
191 size_t nRest, nGot;
192 off_t skipBytes;
193 int nErr;
194
195 ssize_t SendData(int, int, off_t&, size_t) override
196 {
197 return 0;
198 } // nothing to send
199 bool StoreFileData(const char *p, unsigned int count) override
200 {
201 if (count == 0)
202 {
203 m_status=FIST_COMPLETE;
204 return true;
205 }
206
207 if(skipBytes>0)
208 {
209 if(skipBytes>count)
210 {
211 skipBytes-=count;
212 return true;
213 }
214 count-=skipBytes;
215 p+=skipBytes;
216 skipBytes=0;
217 }
218
219 if(!nRest)
220 {
221 m_status=FIST_COMPLETE;
222 return false;
223 }
224 if(count>nRest)
225 count=nRest;
226 memcpy(pRet+nGot, p, count);
227 nGot+=count;
228 nRest-=count;
229 return true;
230 }
231 #define SETERROR { nErr=__LINE__; return false;}
232 bool &m_isFirst;
233 bool DownloadStartedStoreHeader(const header &head, size_t, const char*, bool bRestarted, bool&) override
234 {
235 _cerr(head.frontLine<<endl);
236 m_head = head; // XXX: bloat, only status line and contlen required
237 int st =head.getStatus();
238
239 if(st == 416)
240 return true; // EOF
241
242 if(bRestarted) // throw the head away, the data should be ok
243 return true; // XXX, add more checks?
244
245 if(st != 200 && st != 206)
246 {
247 SETERROR;
248 }
249
250 // validation
251 if (head.h[header::LAST_MODIFIED])
252 {
253 if (m_isFirst)
254 fid.m_ctime = head.h[header::LAST_MODIFIED];
255 else if (fid.m_ctime != head.h[header::LAST_MODIFIED])
256 SETERROR;
257 }
258
259 off_t myfrom(0), myto(0), mylen(0);
260 const char *p=head.h[header::CONTENT_RANGE];
261 if(p)
262 {
263 int n=sscanf(p, "bytes " OFF_T_FMT "-" OFF_T_FMT "/" OFF_T_FMT, &myfrom, &myto, &mylen);
264 if(n<=0)
265 n=sscanf(p, "bytes=" OFF_T_FMT "-" OFF_T_FMT "/" OFF_T_FMT, &myfrom, &myto, &mylen);
266 if(n!=3 // check for nonsense
267 || (m_nSizeSeen>0 && myfrom != m_nSizeSeen-1)
268 || (m_nRangeLimit>=0 && myto > m_nRangeLimit) // too much data?
269 || myfrom<0 || mylen<0
270 )
271 {
272 SETERROR;
273 }
274
275 }
276 else if(st == 200 && head.h[header::CONTENT_LENGTH])
277 mylen = atoofft(head.h[header::CONTENT_LENGTH]);
278
279 // validation
280 if(m_isFirst)
281 fid.m_size = mylen;
282 else
283 if(fid.m_size != mylen)
284 SETERROR;
285
286 skipBytes -= myfrom;
287 if(skipBytes<0)
288 SETERROR;
289 return true;
290 }
291 tFileId &fid;
292 tFitem(char *p, size_t size, off_t start, tFileId &fi, bool &isfirst)
293 : pRet(p), nRest(size),
294 nGot(0), skipBytes(start), nErr(0), m_isFirst(isfirst), fid(fi)
295 {
296 m_bCheckFreshness = false;
297 m_nSizeSeen = start;
298 m_nRangeLimit = g_bGoodServer ? start+size-1 : -1;
299 }
300 };
301
302 {
303 lockguard g(remote_info_cache);
304 map<string, tFileId>::const_iterator it = remote_info_cache.find(path);
305 if (it != remote_info_cache.end())
306 fid = it->second;
307 }
308 tFileId fidOrig=fid;
309
310 tFitem *pFi = new tFitem(retbuf, len, pos, fid, bIsFirst);
311 tFileItemPtr spFi(static_cast<fileitem*>(pFi));
312 dler.AddJob(spFi, &uri, 0, 0, 0, cfg::REDIRMAX_DEFAULT);
313 dler.WorkLoop();
314 int nHttpCode(100);
315 pFi->WaitForFinish(&nHttpCode);
316 bIsFirst=false;
317
318
319 if (m_ftype == rex::FILE_SOLID && fidOrig != fid)
320 {
321 lockguard g(remote_info_cache);
322 remote_info_cache[m_path] = fid;
323 }
324
325 if(nHttpCode==416)
326 return 0; // EOF
327 if(pFi->nErr || !pFi->nGot)
328 return -EIO;
329 return pFi->nGot;
330 }
331
StattDlDescRemote332 int Stat(struct stat &stbuf)
333 {
334 stbuf = statTempl;
335 {
336 lockguard g(remote_info_cache);
337 map<string, tFileId>::const_iterator it = remote_info_cache.find(m_path);
338 if (it != remote_info_cache.end())
339 {
340 stbuf.st_size = it->second.m_size;
341 stbuf.st_mode &= ~S_IFDIR;
342 stbuf.st_mode |= S_IFREG;
343 struct tm tmx;
344 if(header::ParseDate(it->second.m_ctime.c_str(), &tmx))
345 stbuf.st_ctime = mktime(&tmx);
346 _cerr("Using precached\n");
347 return 0;
348 }
349 }
350 // ok, not cached, do the hard way
351
352 dlcon dler(true, 0);
353
354 tHttpUrl uri = proxyUrl;
355 uri.sPath += baseUrl.sHost
356 // + ":" + ( baseUrl.sPort.empty() ? baseUrl.sPort : "80")
357 + baseUrl.sPath + m_path;
358 class tFitemProbe: public fileitem
359 {
360 public:
361 ssize_t SendData(int, int, off_t&, size_t) override
362 {
363 return 0;
364 } // nothing to send
365 bool StoreFileData(const char*, unsigned int) override
366 {
367 return false;
368 }
369 tFitemProbe()
370 {
371 m_bHeadOnly = true;
372 }
373 bool DownloadStartedStoreHeader(const header &head, size_t, const char*,
374 bool bRestart, bool&) override
375 {
376 if(bRestart)
377 return true;
378
379 m_head = head; // XXX: bloat, only status line and contlen required
380 m_status = FIST_COMPLETE;
381 return true;
382 }
383 };
384 auto probe(make_shared<tFitemProbe>());
385 dler.AddJob(probe, &uri, 0, 0, 0, cfg::REDIRMAX_DEFAULT);
386 dler.WorkLoop();
387 int nHttpCode(100);
388 fileitem::FiStatus res = probe->WaitForFinish(&nHttpCode);
389 stbuf.st_size = atoofft(probe->GetHeaderUnlocked().h[header::CONTENT_LENGTH], 0);
390 stbuf.st_mode &= ~S_IFDIR;
391 stbuf.st_mode |= S_IFREG;
392
393 if (res < fileitem::FIST_COMPLETE)
394 return -EIO;
395 else if (nHttpCode == 200)
396 {
397 if (m_ftype == rex::FILE_SOLID) // not caching volatile stuff
398 {
399 lockguard g(remote_info_cache);
400 remote_info_cache[m_path] =
401 tFileId(stbuf.st_size, probe->GetHeaderUnlocked().h[header::LAST_MODIFIED]);
402 }
403 struct tm tmx;
404 if(header::ParseDate(probe->GetHeaderUnlocked().h[header::LAST_MODIFIED], &tmx))
405 stbuf.st_ctime = mktime(&tmx);
406 return 0;
407 }
408 return -ENOENT;
409 }
410 };
411
412
413
414 /// If found as downloadable, present as a file, or become a directory otherwise.
acngfs_getattr(const char * path,struct stat * stbuf)415 static int acngfs_getattr(const char *path, struct stat *stbuf)
416 {
417 if(!path)
418 return -1;
419
420 rex::eMatchType type = rex::GetFiletype(path);
421 _cerr( "type: " << type);
422 if (type == rex::FILE_SOLID || type == rex::FILE_VOLATILE)
423 {
424 if(0 == tDlDescLocal(path, type).Stat(*stbuf))
425 return 0;
426 if(0 == tDlDescRemote(path, type).Stat(*stbuf))
427 return 0;
428 }
429
430 //ldbg("Be a directory!");
431 memcpy(stbuf, &statTempl, sizeof(statTempl));
432 stbuf->st_mode &= ~S_IFMT; // delete mode flags and set them as needed
433 stbuf->st_mode |= S_IFDIR;
434 stbuf->st_size = 4;
435 return 0;
436 }
437
acngfs_fgetattr(const char * path,struct stat * stbuf,struct fuse_file_info * fi)438 static int acngfs_fgetattr(const char *path, struct stat *stbuf,
439 struct fuse_file_info *fi)
440 {
441 // FIXME: reuse the con later? or better not, size might change during operation
442 return acngfs_getattr(path, stbuf);
443 }
444
acngfs_access(const char * path,int mask)445 static int acngfs_access(const char *path, int mask)
446 {
447 // non-zero (failure) when trying to write
448 return mask&W_OK;
449 }
450
acngfs_readlink(const char * path,char * buf,size_t size)451 static int acngfs_readlink(const char *path, char *buf, size_t size)
452 {
453 return -EINVAL;
454 }
455
acngfs_opendir(const char * path,struct fuse_file_info * fi)456 static int acngfs_opendir(const char *path, struct fuse_file_info *fi)
457 {
458 // let FUSE manage directories
459 return 0;
460 }
461
acngfs_readdir(const char * path,void * buf,fuse_fill_dir_t filler,off_t offset,struct fuse_file_info * fi)462 static int acngfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
463 off_t offset, struct fuse_file_info *fi)
464 {
465 return -EPERM;
466 }
467
acngfs_releasedir(const char * path,struct fuse_file_info * fi)468 static int acngfs_releasedir(const char *path, struct fuse_file_info *fi)
469 {
470
471 return 0;
472 }
473
acngfs_mknod(const char * path,mode_t mode,dev_t rdev)474 static int acngfs_mknod(const char *path, mode_t mode, dev_t rdev)
475 {
476 return -EROFS;
477 }
478
acngfs_mkdir(const char * path,mode_t mode)479 static int acngfs_mkdir(const char *path, mode_t mode)
480 {
481 return -EROFS;
482 }
483
acngfs_unlink(const char * path)484 static int acngfs_unlink(const char *path)
485 {
486 return -EROFS;
487 }
488
acngfs_rmdir(const char * path)489 static int acngfs_rmdir(const char *path)
490 {
491 return -EROFS;
492 }
493
acngfs_symlink(const char * from,const char * to)494 static int acngfs_symlink(const char *from, const char *to)
495 {
496 return -EROFS;
497 }
498
acngfs_rename(const char * from,const char * to)499 static int acngfs_rename(const char *from, const char *to)
500 {
501 return -EROFS;
502 }
503
acngfs_link(const char * from,const char * to)504 static int acngfs_link(const char *from, const char *to)
505 {
506 return -EROFS;
507 }
508
acngfs_chmod(const char * path,mode_t mode)509 static int acngfs_chmod(const char *path, mode_t mode)
510 {
511 return -EROFS;
512 }
513
acngfs_chown(const char * path,uid_t uid,gid_t gid)514 static int acngfs_chown(const char *path, uid_t uid, gid_t gid)
515 {
516 return -EROFS;
517 }
518
acngfs_truncate(const char * path,off_t size)519 static int acngfs_truncate(const char *path, off_t size)
520 {
521 return -EROFS;
522 }
523
acngfs_ftruncate(const char * path,off_t size,struct fuse_file_info * fi)524 static int acngfs_ftruncate(const char *path, off_t size,
525 struct fuse_file_info *fi)
526 {
527 return -EROFS;
528 }
529
acngfs_utime(const char * path,struct utimbuf * buf)530 static int acngfs_utime(const char *path, struct utimbuf *buf)
531 {
532 return -EROFS;
533 }
534
535 //lockable mxTest;
536
acngfs_open(const char * path,struct fuse_file_info * fi)537 static int acngfs_open(const char *path, struct fuse_file_info *fi)
538 {
539 //lockguard g(&mxTest);
540
541 if (fi->flags & (O_WRONLY|O_RDWR|O_TRUNC|O_CREAT))
542 return -EROFS;
543
544 tDlDesc *p(nullptr);
545 struct stat stbuf;
546 rex::eMatchType ftype = rex::GetFiletype(path);
547
548 try
549 {
550 // ok... if that is a remote object, can we still use local access instead?
551 if(!altPath.empty() && rex::FILE_SOLID == ftype)
552 {
553 p = new tDlDescLocal(path, ftype);
554 if(p)
555 {
556 if(0==p->Stat(stbuf))
557 goto desc_opened;
558 delete p;
559 p=nullptr;
560 }
561 }
562
563
564 p=new tDlDescRemote(path, ftype);
565 if(!p) // running exception-free?
566 return -EIO;
567 if(0!=p->Stat(stbuf))
568 {
569 delete p;
570 return -EIO;
571 }
572 }
573 catch(std::bad_alloc&)
574 {
575 return -EIO;
576 }
577
578 desc_opened:
579
580 fi->fh = (uintptr_t) p;
581 return 0;
582 }
583
584
acngfs_read(const char * path,char * buf,size_t size,off_t offset,struct fuse_file_info * fi)585 static int acngfs_read(const char *path, char *buf, size_t size, off_t offset,
586 struct fuse_file_info *fi)
587 {
588 auto p=(tDlDesc*) fi->fh;
589 return p->Read(buf, path, offset, size);
590 }
591
acngfs_write(const char * path,const char * buf,size_t size,off_t offset,struct fuse_file_info * fi)592 static int acngfs_write(const char *path, const char *buf, size_t size,
593 off_t offset, struct fuse_file_info *fi)
594 {
595 return -EBADF;
596 }
597
acngfs_statfs(const char * path,struct statvfs * stbuf)598 static int acngfs_statfs(const char *path, struct statvfs *stbuf)
599 {
600 memcpy(stbuf, &stfsTemp, sizeof(*stbuf));
601 return 0;
602 }
603
acngfs_release(const char * path,struct fuse_file_info * fi)604 static int acngfs_release(const char *path, struct fuse_file_info *fi)
605 {
606 if(fi->fh)
607 delete (tDlDesc*)fi->fh;
608 return 0;
609 }
610
acngfs_fsync(const char * path,int isdatasync,struct fuse_file_info * fi)611 static int acngfs_fsync(const char *path, int isdatasync,
612 struct fuse_file_info *fi)
613 {
614 return 0;
615 }
616
617
618 struct fuse_operations_compat25 acngfs_oper;
619
my_fuse_main(int argc,char ** argv)620 int my_fuse_main(int argc, char ** argv)
621 {
622 #ifdef HAVE_DLOPEN
623 auto pLib = dlopen("libfuse.so.2", RTLD_LAZY);
624 if(!pLib)
625 {
626 cerr << "Couldn't find libfuse.so.2" <<endl;
627 return -1;
628 }
629 auto myFuseMain = (decltype(&fuse_main_real_compat25)) dlsym(pLib, "fuse_main_real_compat25");
630 if(!myFuseMain)
631 {
632 cerr << "Error loading libfuse.so.2" <<endl;
633 return -2;
634 }
635 return (*myFuseMain) (argc, argv, &acngfs_oper, sizeof(acngfs_oper));
636 #else
637 #warning dlopen not available
638 return fuse_main(argc, argv, &acngfs_oper);
639 #endif
640 }
641
_ExitUsage()642 void _ExitUsage() {
643 cerr << "USAGE: acngfs BaseURL ProxyHost MountPoint [FUSE Mount Options]\n"
644 << "examples:\n\t acngfs http://ftp.uni-kl.de/debian cacheServer:3142 /var/local/aptfs\n"
645 << "\t acngfs http://ftp.uni-kl.de/debian localhost:3142 /var/cache/apt-cacher-ng/debrep /var/local/aptfs\n\n"
646 << "FUSE mount options summary:\n\n";
647 char *argv[] = {strdup("..."), strdup("-h")};
648 my_fuse_main( _countof(argv), argv);
649 exit(EXIT_FAILURE);
650 }
651
652 #define barf(x) { cerr << endl << "ERROR: " << x <<endl; exit(1); }
653 #define erUsage { _ExitUsage(); }
654
main(int argc,char * argv[])655 int main(int argc, char *argv[])
656 {
657 using namespace acng;
658 memset(&acngfs_oper, 0, sizeof(acngfs_oper));
659
660 acngfs_oper.getattr = acngfs_getattr;
661 acngfs_oper.fgetattr = acngfs_fgetattr;
662 acngfs_oper.access = acngfs_access;
663 acngfs_oper.readlink = acngfs_readlink;
664 acngfs_oper.opendir = acngfs_opendir;
665 acngfs_oper.readdir = acngfs_readdir;
666 acngfs_oper.releasedir = acngfs_releasedir;
667 acngfs_oper.mknod = acngfs_mknod;
668 acngfs_oper.mkdir = acngfs_mkdir;
669 acngfs_oper.symlink = acngfs_symlink;
670 acngfs_oper.unlink = acngfs_unlink;
671 acngfs_oper.rmdir = acngfs_rmdir;
672 acngfs_oper.rename = acngfs_rename;
673 acngfs_oper.link = acngfs_link;
674 acngfs_oper.chmod = acngfs_chmod;
675 acngfs_oper.chown = acngfs_chown;
676 acngfs_oper.truncate = acngfs_truncate;
677 acngfs_oper.ftruncate = acngfs_ftruncate;
678 acngfs_oper.utime = acngfs_utime;
679 // acngfs_oper.create = acngfs_create;
680 acngfs_oper.open = acngfs_open;
681 acngfs_oper.read = acngfs_read;
682 acngfs_oper.write = acngfs_write;
683 acngfs_oper.statfs = acngfs_statfs;
684 acngfs_oper.release = acngfs_release;
685 acngfs_oper.fsync = acngfs_fsync;
686
687 umask(0);
688
689 for(int i = 1; i<argc; i++)
690 if(argv[i] && 0==strcmp(argv[i], "--help"))
691 erUsage;
692
693 if(argc<4)
694 barf("Not enough arguments, try --help.\n");
695
696 cfg::agentname = "ACNGFS";
697 cfg::agentheader="User-Agent: ACNGFS\r\n";
698 cfg::requestapx = "User-Agent: ACNGFS\r\nX-Original-Source: 42\r\n";
699 #ifdef SPAM
700 cfg::debug=0xff;
701 cfg::verboselog=1;
702 #endif
703
704 if(argv[1] && baseUrl.SetHttpUrl(argv[1]))
705 {
706 #ifdef VERBOSE
707 cout << "Base URL: " << baseUrl.ToString()<<endl;
708 #endif
709 }
710 else
711 {
712 cerr << "Invalid base URL, " << argv[1] <<endl;
713 exit(EXIT_FAILURE);
714 }
715 // FUSE adds starting / already, drop ours if present
716 trimBack(baseUrl.sPath, "/");
717
718 if(argv[2] && proxyUrl.SetHttpUrl(argv[2]))
719 {
720 /*if(proxyUrl.GetPort().empty())
721 proxyUrl.sPort="3142";
722 */
723 }
724 else
725 {
726 cerr << "Invalid proxy URL, " << argv[2] <<endl;
727 exit(EXIT_FAILURE);
728 }
729
730 // all parameters processed, forwarded to fuse call below
731
732 acng::rex::CompileExpressions();
733
734 #if 0//def SPAM
735 {
736 fuse_file_info fi = {0};
737 const char *dingsda="/dists/unstable/InRelease";
738 acngfs_open(dingsda, &fi);
739 char buf[165536];
740 off_t pos=0;
741 for(;0 < acngfs_read(dingsda, buf, sizeof(buf), pos, &fi); pos+=sizeof(buf)) ;
742 return 0;
743 }
744 #endif
745
746 unsigned int nMyArgCount = 2; // base url, proxy host
747 // alternative path supplied in the next argument?
748 if(argc > 4 && argv[4] && argv[4][0] != '-' ) // 4th argument is not an option?
749 {
750 nMyArgCount=3;
751 altPath = argv[3];
752 }
753
754
755
756 // test mount point
757 const char *mpoint = argv[nMyArgCount+1];
758 if(stat(mpoint, &statTempl) || statfs(mpoint, &stfsTemp))
759 barf(endl << "Cannot access " << mpoint);
760 if(!S_ISDIR(statTempl.st_mode))
761 barf(endl<< mpoint << " is not a directory.");
762
763 // skip our arguments, keep those for fuse including mount point and argv[0] at the right place
764 argv[nMyArgCount]=argv[0]; // application path
765 argv=&argv[nMyArgCount];
766 argc-=nMyArgCount;
767 return my_fuse_main(argc, argv);
768 }
769
770 #ifndef DEBUG
771 namespace acng {
772 // for the uber-clever GNU linker and should be removed by strip again
773 namespace log
774 {
flush()775 void flush() {};
misc(const string & s,const char)776 void misc(const string &s, const char )
777 {
778 #ifdef SPAM
779 cerr << s << endl;
780 #endif
781 }
782
err(const char * s,const char * z)783 void err(const char *s, const char* z)
784 {
785 #ifdef SPAM
786 cerr << s << endl << z << endl;
787 #endif
788
789 };
790 }
791 }
792 #endif
793