portalfsとFUSEで独自ファイルシステムを実装しよう

以下のテキストは、執筆時当時の情報を元に書いたものであり、 現在の情勢にそぐわないことを含む場合があるので注意されたい。 また、テキストは最終提出原稿で校正を経る前のものなので、実際にOSM 本誌に記載されたものとは異なる。誤字脱字等そのままである。

致命的な誤り以外は加筆修正等は行なわないので情報の鮮度に気をつけつつ 利用して欲しい。

目次


======================================================================
Part4: portalfsFUSE
======================================================================









Part4 portalfs  FUSE

3
 kernel hacker :-)

	* (portalfs+)
	  portalfs
	  
	  

	* (portalfs+C+Ruby)
	  portalfs
	  
	  C

	* (FUSE+Ruby)
	  FUSE
	  (f)open()readdir()(f)stat()
	  


 - portalfs+




WebHTMLHTML
CGISSIPHP
Web
Web
FTP


portalfsCGI
Web

FreeBSD/NetBSD portalfs 


FreeBSD /usr/share/examples/portal NetBSD
/usr/share/examples/mount_portal  portal.conf 
portalfs /p 

	# cp /usr/share/examples/*portal/portal.conf /etc
	# mount_portalfs /etc/portal.conf /p






http://www.ageun.com/daily/furtune.html
 
---[ ]------------------------------------------------------------
Web
/usr/games/foretune 
----------------------------------------------------------------------

120112id
CGI

 http://www.ageun.com/cgi-bin/dairy2/index.cgi?id=01
 http://www.ageun.com/cgi-bin/dairy2/index.cgi?id=02
	:
	:
   http://www.ageun.com/cgi-bin/dairy2/index.cgi?id=12

URLWeb
w3m 

---[ ]------------------------------------------------------------
http://w3m.sourceforge.net/index.ja.html
FreeBSD ports /usr/ports/www/w3m-m17n-img
NetBSD pkgsrc /usr/pkgsrc/www/w3m-m17n  make install 

----------------------------------------------------------------------


portalfsPATH



w3m-dump 
 /usr/local/bin/getzodf 


---[ ]--------------------------------------------------------
[ /usr/local/bin/getzodf ]

#!/bin/sh
PATH=/usr/local/bin:/usr/pkg/bin:$PATH
CGI=http://www.ageun.com/cgi-bin/dairy2/index.cgi
id=01
case "$1" in
  oh*|ar*)	id=01 ;;
  ou*|ta*)	id=02 ;;
  fu*|ge*)	id=03 ;;
  ka*|can*)	id=04 ;;
  sh*|si*|le*)	id=05 ;;
  ot*|v*)	id=06 ;;
  te*|li*)	id=07 ;;
  sas*|sc*)	id=08 ;;
  i*|sag*)	id=09 ;;
  y*|cap*)	id=10 ;;
  m*|aq*)	id=11 ;;
  u*|p*)	id=12 ;;
  [1-9]|[12][0-9]) id=$1 ;;
esac
w3m -dump "$CGI?id=$id" | colrm 1 14
----------------------------------------------------------------------

1


:	% getzodf uoza			# 

  * FreeBSD portalfs pipe 

    pipe
    
    /p/pipe"/usr/local/bin/getzodf uoza"
    
    

	% ln -s /p/pipe"/usr/local/bin/getzodf uoza" .uoza

    

  * NetBSD portalfs rfilter 

    rfilterportal.conf
    FreeBSDpipe
    
    /etc/portal.conf 

	/zodiac		rfilter /zodiac /usr/local/bin/getzodf %s

     portal.conf  /p 

	# umount /p
	# mount_portal /etc/portal.conf /p

    (getzodf)
    

	% cat /p/zodiac/uo







JPG
portalfs

(signature)

portalfs







 portalfs


FreeBSDmount_potalfs

-----------------------------------------------------------------
# cd /usr/src/usr.sbin/mount_portalfs
# ls -l *.[ch]
-rw-r--r--  1 root  wheel  4911 May 11  2005 activate.c
-rw-r--r--  1 root  wheel  7259 Aug  7  2004 conf.c
-rw-r--r--  1 root  wheel  2543 Mar 11  2005 cred.c
-rw-r--r--  1 root  wheel  6440 Mar 11  2005 mount_portalfs.c
-rw-r--r--  1 root  wheel  1935 Aug  7  2004 pathnames.h
-rw-r--r--  1 root  wheel  3149 Mar 11  2005 portald.h
-rw-r--r--  1 root  wheel  2111 Mar 11  2005 pt_conf.c
-rw-r--r--  1 root  wheel  2081 Aug  7  2004 pt_exec.c
-rw-r--r--  1 root  wheel  2950 May 20  2005 pt_file.c
-rw-r--r--  1 root  wheel  5777 Mar 11  2005 pt_pipe.c
-rw-r--r--  1 root  wheel  4226 Aug  7  2004 pt_tcp.c
-rw-r--r--  1 root  wheel  6816 Aug  7  2004 pt_tcplisten.c
-----------------------------------------------------------------
(NetBSD /usr/src/sbin/mount_portal)

portal.conf(fs, pipe, tcp, tcplisten)
C5000

TCP
TCP


	FILE *fp;
	fp = fopen("file", "r");




	* FreeBSDportalfs
	* 
	  NetBSDrfilter(pt_filter.c)
	* rfilter
	  (rfilterwfilter)



	* 
	* 

ftpWebDAV
RDB
 

---[ ]------------------------------------------------------------
read/write(stat)

----------------------------------------------------------------------



	1. NetBSD pt_filter.c FreeBSD
	2. rfilter(wfilter)
	3. FreeBSD pt_conf.c  rfilter 
	4. 



1FreeBSDNetBSDpt_filter.c

FreeBSD /usr/src  
NetBSD
mount_portal 

---[ ]------------------------------------------------------------
sysinstallConfigure  Distributions  src(Sources for everything)

----------------------------------------------------------------------

% cvs -d :pserver:anoncvs@sup.jp.netbsd.org:/cvs/cvsroot login
( anoncvs )

% cvs -d :pserver:anoncvs@sup.jp.netbsd.org:/cvs/cvsroot \
	co -r netbsd-3 src/sbin/mount_portal

% cd src/sbin/mount_portal

pt_filter.c FreeBSD
/usr/src/usr.sbin/mount_portalfs 

% su		()
# cp -i pt_filter.c /usr/src/usr.sbin/mount_portalfs

2rfilterwfilter

 NetBSDpt_filter.c rfilterwfilter
wfilter
 portal_wfilter()  

---[ ]------------------------------------------------------------
 wfilter 

----------------------------------------------------------------------



# cd /usr/src/usr.sbin/mount_portalfs
# vi +/portal_wfilter/-1 pt_filter.c

  (int portal_wfilter
    dG )

rfilter
rwfiltervi

	:%s/rfilter/rwfilter/w [Return]




/popen[Return]popen

	fp = popen(cmd, "r");

 "r"  "w+" 

	fp = popen(cmd, "w+");

 :wq [Return] 

3pt_conf.c  rfilter 

pt_conf.c  

---[ ]--------------------------------------------------------
provider providers[] = {
        { "exec",       portal_exec },
        { "file",       portal_file },
        { "pipe",       portal_pipe },
        { "tcp",        portal_tcp },
        { "tcplisten",  portal_tcplisten },
        { 0, 0 }
};
----------------------------------------------------------------------

tcplistenrwfilter  

---[ ]--------------------------------------------------------
pt_conf.c 


provider providers[] = {
        { "exec",       portal_exec },
        { "file",       portal_file },
        { "pipe",       portal_pipe },
        { "tcp",        portal_tcp },
        { "tcplisten",  portal_tcplisten },
        { "rwfilter",   portal_rwfilter },
        { 0, 0 }
};
----------------------------------------------------------------------

 portald.h
 portal_rwfilter() 
tcplisten 

---[ ]--------------------------------------------------------
portald.h

extern int portal_rwfilter(struct portal_cred *,
                                char *key, char **v, int so, int *fdp);
----------------------------------------------------------------------

4

Makefile pt_filter.c  

---[ ]--------------------------------------------------------
Makfile

SRCS=   mount_portalfs.c activate.c conf.c cred.c getmntopts.c pt_conf.c \
        pt_exec.c pt_file.c pt_pipe.c pt_tcp.c pt_tcplisten.c pt_filter.c
		  	    	      	       		     ------------
							
----------------------------------------------------------------------



----------------------------------------------------------------------
pt_filter.o(.text+0x10): In function `portal_rwfilter':
: undefined reference to `lose_credentials'
*** Error code 1

Stop in /usr/src/usr.sbin/mount_portalfs.
----------------------------------------------------------------------

 user_credential 
NetBSDFreeBSDFreeBSD pt_pipe.c 

 (CD-ROM
)

---[ ]--------------------------------------------------------
--- pt_filter.c.orig    Sat Aug 12 19:38:48 2006
+++ pt_filter.c Sat Aug 12 19:47:26 2006
@@ -91,13 +91,14 @@
        char   *path;
        FILE   *fp;
        int     error = 0;
+       struct portal_cred save_area;
 
        /* We don't use this parameter. */
        (void) kso;
 
-       error = lose_credentials(pcr);
-       if (error != 0)
-               return error;
+       /* Swap priviledges. */
+       if (set_user_credentials(pcr, &save_area) < 0)
+               return (errno);
 
 #ifdef DEBUG
        fprintf(stderr, "rwfilter:  Got key %s\n", key);
@@ -162,6 +163,9 @@
                        }
                }
        }
+       /* Re-establish our priviledges. */
+       if (restore_credentials(&save_area) < 0)
+               error = errno;
        if (error == 0)
                fdp[0] = fileno(fp);
        return (errno);
----------------------------------------------------------------------


make portalfs


# make all install

rwfilterZIP

Windows XPZIP
ZIP
()rwfilter




	/p/zip/foo/bar.zip/baz
	open
	/foo/bar.zip  baz 

	open
	/foo/bar.zip  baz 

/etc/portal.conf 


	zip/	rwfilter zip/	 %s


 /p/zip/ 
/p/zip/foo/bar.zip/baz  foo/bar.zip/baz 




	(dirname)ZIP
	(basename)
	
	

Ruby
 

---[ ]--------------------------------------------------------
/usr/local/bin/pzipio.rb

#!/usr/local/bin/ruby

exit if Process.euid == 0	# root:)
target="/"+ARGV[0]		# /
zipname=File.dirname(target)
filename=File.basename(target)
ENV["PATH"] = "/bin:/usr/bin:/usr/local/bin"
TMPDIR=ENV["TMPDIR"] || "/tmp"
trial=10

r = IO.select([STDIN], [STDIN], nil, nil)
if r[0][0]		# STDIN
  tmpdir=nil		# 
  while trial > 0
    f=sprintf("%s/%s.%s.%d", TMPDIR, "pzipio", $$, trial-=1)
    begin		# 
      Dir.mkdir(f, 0700)# raise ERROR if exists
      tmpdir=f
      break
    rescue
      next
    end
  end
  if tmpdir && test(?d, tmpdir) && test(?w, tmpdir)
    Dir.chdir(tmpdir){|dir|
      open(filename, "w") do |out| # STDIN
	out.print STDIN.readlines  # 
      end
      system("zip #{zipname} #{filename} > /dev/null")
    }
    File.unlink(tmpdir+"/"+filename)
    Dir.rmdir(tmpdir)	# 
  end
else			# ZIP
  if filename == ".list"	# .list 
    system("unzip -v #{zipname}")
  else				# 
    system("unzip -cqq #{zipname} #{filename}")
  end
end
----------------------------------------------------------------------

 /usr/local/bin/pzipio.rb 
 /etc/portal.conf 


---[ ]--------------------------------------------------------
/etc/portal.conf1

	zip/	rwfilter zip/	/usr/local/bin/portal.conf %s
----------------------------------------------------------------------

/p 

# umount /p
# mount_portalfs /etc/portal.conf /p


rwfilter/ZIP

ZIP portalfs 

ZIP

----------------------------------------------------------------------
% mkdir /tmp/ziptest
% cd /tmp/ziptest
% jcal
      2006  9       
              
                      1   2 
( 3)  4   5   6   7   8   9 
(10) 11  12  13  14 (15) 16 
(17) 18  19  20  21  22 (23)
(24) 25  26  27  28  29  30 

% jcal > ca
% ls -lF
total 1
-rw-r--r--  1 yuuji  wheel  135 Aug 13 15:18 ca
% zip -m a.zip ca		# (-m)
  adding: ca (deflated 42%)
% ls -lF
total 1
-rw-r--r--  1 yuuji  wheel  271 Aug 13 15:18 a.zip
% unzip -v a
Archive:  a.zip
 Length   Method    Size  Ratio   Date   Time   CRC-32    Name
--------  ------  ------- -----   ----   ----   ------    ----
     232  Defl:N      135  42%  08-13-06 15:18  75f95a44  ca
--------          -------  ---                            -------
     232              135  42%                            1 file
----------------------------------------------------------------------

/tmp/ziptest/a.zip  ca portalfs
/p/zip/tmp/ziptest/a.zip/ca 

----------------------------------------------------------------------
% cat /p/zip/tmp/ziptest/a.zip/ca
      2006  9       
              
                      1   2 
( 3)  4   5   6   7  =8=  9 
(10) 11  12  13  14 (15) 16 
(17) 18  19  20  21  22 (23)
(24) 25  26  27  28  29  30 
----------------------------------------------------------------------

ca

----------------------------------------------------------------------
% uname -a > /p/zip/tmp/ziptest/a.zip/ca
()
% unzip -cqq a.zip ca
FreeBSD lead.yk.gentei.org 6.1-STABLE FreeBSD 6.1-STABLE #2: Tue Aug  1 14:56:51 JST 2006
yuuji@lead.yk.gentei.org:/usr/src/sys/i386/compile/VMWARE  i386
----------------------------------------------------------------------



 rzipio.rb 


 pt_filter.c 
portalfs

FUSE



 FUSE(Linux)


FUSE(fuse-2.5.3.tar.gz)example/ 
C hello.c 

	*  hello 
	* Hello World!

FUSE
 struct fuse_operations 
FUSEAPI
fuse.h  fuse_operations 


C
C
FUSERuby
Ruby
RubyRuby 1.8
Ruby1.8.4

---[ ]------------------------------------------------------------
 ruby ruby-devel 
 fuse-devel 
----------------------------------------------------------------------

FUSERuby
http://rubyforge.org/projects/fusefs

fusefs-0.6.0.tar.gz 



% tar fusefs-0.6.0.tar.gz
% cd fusefs-0.6.0
% ruby setup.rb config
% ruby setup.rb setup
% sudo ruby setup.rb install

FUSEFSRuby
ruby setup.rb setup
 libfuse.so 
ruby setup.rb
config ext/Makefile 

	% vi "+/^LIBS" Makefile
--------------------------------------------------------------
LIBS =  -lfuse  -ldl -lcrypt -lm   -lc -Wl,-R/usr/local/lib -L/usr/local/lib
     		     	     	      --------------------------------------
				
--------------------------------------------------------------

 libfuse.so  /usr/local/lib 


sample/ 
Ruby

----------------------------------------------------------------------
% cd sample
% mkdir hellotest
% ruby hello.rb hellotest &
% ls -lF hellotest
 0
-r--r--r--    1 yuuji    yuuji           0 Aug 13 08:18 hello.txt
% cat hellotest/hello.txt
Hello, World!
----------------------------------------------------------------------

 hello.rb 

--------------------------------------------------------------
% kill %ruby
--------------------------------------------------------------

Ruby/fusefs 

	!!!!!!
	Ruby/fusefs API.txtfusefs
	FUSEFUSE
	ruby kill -KILL 
	 fusermount -u 



FUSEhello.c



	* 
	*  Sunday, Monday, ...,
	   Saturday 7
	* 


----------------------------------------------------------------------
% weekfs.rb ~/week &
% ls week
Friday  Monday  Saturday  Sunday  Thursday  Tuesday  Wednesday
% cat week/Friday

----------------------------------------------------------------------

Ruby/fusefs 
Ruby/fusefs  sample/hello.rb  API.txt 


	*  contents 
	*  file? 
	*  read_file 



* contents
  Friday Monday Saturday Sunday Thursday Tuesday Wednesday
  7
  def contents(path)
    %w(Friday Monday Saturday Sunday Thursday Tuesday Wednesday)
  end
  

* file?
  file?
  Sunday "/Sunday" 
  (/)7
  
  def file?(path)
    path = path[1..-1]  # 1
    %w(Friday Monday Saturday Sunday Thursday Tuesday Wednesday).index(path)
  end
  index
  nil

* read_file
  7
  def read_file(path)
    jwday = %w(      )
    ix = file?(path)	# 
    jwday[ix] if ix	# 
  end

7
Ruby initialize 
 

---[ ]--------------------------------------------------------
#!/usr/bin/env ruby
# EUC

require 'fusefs'

class WeekDir
  def initialize
    # 7
    @wday = %w(Sunday Monday Tuesday Wednesday Thursday Friday Saturday)
    @jwday = %w(      )
  end
  def contents(path)
    @wday
  end

  def file?(path)
    path = path[1..-1]
    @wday.index(path)
  end

  def read_file(path)
    ix = file?(path)
    @jwday[ix] if ix
  end

  def size(path)
    c = read_file(path)
    c ? c.length : 0
  end
end

weekdir = WeekDir.new
FuseFS.set_root( weekdir )

# Mount under a directory given on the command line.
FuseFS.mount_under ARGV.shift
FuseFS.run
----------------------------------------------------------------------




portalfs
FUSE
ls
FUSE
unpackfshttp://www.nongnu.org/unpackfs/ 





FUSE-API
acvfsRuby/fusefs
Ruby

 (CD-ROM
)

---[ ]------------------------------------------------------------
#!/usr/bin/env ruby
#
# acvfs.rb: Archive FileSystem Based on FUSE/Ruby
# (C)2006 by HIROSE, Yuuji [yuuji@yatex.org]
#
# Usage: acvfs.rb mountpoint
#
# This file system provides zip/tar.gz/tar.bz2 auto inspection.
#
# Requirements:
# http://fuse.sourceforge.net/ - Filesystem in Userspace
# http://rubyforge.org/projects/fusefs - Ruby binding of FUSE-FS
#
# Todo:
#  Support other archivers than arc, arj, rar, ... and so on.
#  But it might be easy.  Try it by yourself.
#
require 'fusefs'

class ArchiveDir
  def initialize
    @cachedacv = Hash.new
  end
  def zip?(path)
    /\.zip/i =~ path
  end
  def targz?(path)
    /\.(tar\.gz|tgz)/i =~ path
  end
  def tarbz2?(path)
    /\.(tar\.bz2|tbz)/i =~ path
  end
  def acv?(path)  # path is archived file or within it, or not
    zip?(path) || targz?(path) || tarbz2(path)
  end
  def acvfile?(path) # path IS archived file, or not
    /\.(zip|tar\.(gz|bz2))$/i =~ path
  end
  def listcmd(arc)
    # Return the array of [ListingCmd, Regexp, [$size, $name], EndRattern]
    if zip?(arc)
      ["| unzip -v \"#{arc}\" | tail +4",
        /(\d+) +(\d+)% +(\d+)-(\d+)-(\d+) +(\d\d):(\d\d) .* (\S+)$/,
        [1, 8],
        /^---/]
    elsif targz?(arc)||tarbz2?(arc)
      c = targz?(arc) ? "z" : "j"
      ["| tar v#{c}tf \"#{arc}\"",
        /(\d+) +(\d{4})-(\d+)-(\d+) +(\d\d):(\d\d):(\d\d) +(\S+)$/,
        [1, 8],
        /^$/]
    end
  end

  # cache all filenames, sizes and whether they are directory
  def acvinfo(acv)
    %r,(.*\.(zip|tar\.(gz|bz2)))(/(.*))?, =~ acv
    dir = $1
    prefix = $5
    if !@cachedacv[acv]
      @cachedacv[acv] = Hash.new
      listinfo = listcmd(dir)
      open(listinfo[0], "r") do |zls|
        while line = zls.gets
          case line
          when listinfo[3]
            break
          when listinfo[1]
            md = Regexp.last_match
            dirp = nil          # is_directory flag
            name = md[listinfo[2][1]]
            if prefix
              next unless name.index(prefix) == 0
              next if name == prefix
              name.sub!(prefix+"/", "") # strip prefix directory
            end
            next if name == ""     # skip prefix dir itself
            next if %r,/., =~ name # skip files in subdirectory
            if %r,/$, =~ name
              name.chop!
              dirp = true
            end
            @cachedacv[acv][name] = Hash.new
            @cachedacv[acv][name]['dir'] = dirp
            @cachedacv[acv][name]['size'] = md[listinfo[2][0]].to_i
            # p ["set", acv, name, dir, md[listinfo[2][0]].to_i, dirp]
          end
        end
      end
    end
    @cachedacv[acv]
  end
  # Return the list of files in directory: path
  def contents(path)
    mntpt=ARGV[0].gsub(%r|/+|, "/")
    if test(?d, path)
      Dir.entries(path).select{|f|
        n = (path+"/"+f).gsub(%r|/+|, "/")
        next if ARGV[0] == n
        test(?d, n) || test(?f, n) && acvfile?(f)
      }
    elsif acv?(path)
      acvinfo(path).keys
    end
  end

  def file?(path)
    test(?f, path) ||
      if acv?(path)
        acv = File.dirname(path)
        file = File.basename(path)
        acvinfo(acv).has_key?(file) && !acvinfo(acv)[file]['dir']
      end

  end
  def executable?(path)
    test(?d, path) ? test(x, path) : false # shoud be safer
  end
  def directory?(path)
    test(?d, path) || acvfile?(path) ||
      if acv?(path)
        acv = File.dirname(path)
        file = File.basename(path)
        acvinfo(acv)[file]['dir']
      end
  end
  def can_write?(path)
    false
  end

  def read_file(path)
    %r,(.*\.(tar\.(gz|bz2)|zip))(/(.*))?, =~ path
    acv, file = $1, $5
    if acv && file
      cmd = if zip?(acv)
              "unzip -cqq \"#{acv}\" \"#{file}\""
            elsif targz?(acv)
              "tar zxOf \"#{acv}\" \"#{file}\""
            elsif tarbz2?(acv)
              "tar jxOf \"#{acv}\" \"#{file}\""
            end
      IO.popen(cmd, "r").read
    else
      "Not an archived file\n"
    end
  end

  def size(path)
    acv = File.dirname(path)
    file = File.basename(path)
    if acv?(acv)
      acvinfo(acv)[file]['size']
    else
      test(?s, path)
    end
  end
end

adir = ArchiveDir.new
FuseFS.set_root( adir )

# Mount under a directory given on the command line.
FuseFS.mount_under ARGV[0]
FuseFS.run
----------------------------------------------------------------------

acvfs.rb 


	* 
	* 
	* ziptar.gz
	  
	  

acvfs.rb on demand ziptar


acvfs.rb /z 
FedoraCore 5

----------------------------------------------------------------------
cb1{yuuji}% sudo mkdir /z
cb1{yuuji}% sudo chown yuuji /z
cb1{yuuji}% ./acvfs.rb /z &		()

cb1{yuuji}% mount -t fuse
/dev/fuse on /z type fuse (rw,nosuid,nodev,user=yuuji)
cb1{yuuji}% ls -F /z
bin/   etc/   lost+found/  misc/  opt/   sbin/     sys/  var/
boot/  home/  media/       mnt/   proc/  selinux/  tmp/
dev/   lib/   memory/      net/   root/  srv/      usr/

( / )

cb1{yuuji}% ls -F /z/home/yuuji/make
fusefs-0.6.0/  fusefs-0.6.0.tar.gz/  gmail/  python/

(fusefs-0.6.0.tar.gz )

cb1{yuuji}% ls -F /home/yuuji/make
acvfs.rb*  fusefs-0.6.0/  fusefs-0.6.0.tar.gz  gmail/  python/
( ~/make 32)

cb1{yuuji}% ls -F /z/home/yuuji/make/fusefs-0.6.0.tar.gz
fusefs-0.6.0/

cb1{yuuji}% ls -F /z/home/yuuji/make/fusefs-0.6.0.tar.gz/fusefs-0.6.0
API.txt    Changes.txt  README.txt  ext/      lib/     setup.rb
COPYRIGHT  Makefile     TODO        foo.yaml  sample/
----------------------------------------------------------------------



---[ ]------------------------------------------------------------
%image acvfs-onFC5.png
----------------------------------------------------------------------
fusefs-0.6.0.tar.gz 






OS





---[ ]------------------------------------------------------------
----------------------------------------------------------------------


yuuji@gentei.org
Fingerprint16 = FF F9 FF CC E0 FE 5C F7 19 97 28 24 EC 5D 39 BA
HIROSE Yuuji - ASTROLOGY / BIKE / EPO / GUEST BOOK / YaTeX [Tweet]