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