(1)apr_bucket_file情報
FILEデータバケットを見てみる。FILEデータバケットの格納データはapr_bucket_file情報だ。
apr_bucket_file情報は、以下の情報を持っていて、バケット情報のタイプ依存変数 data にセットされる。
共有リソース用の参照回数の情報
FILEバケット情報の処理対象のapr_file_t情報がセットされる
mmapが使用できるかどうかのフラグ。
centos環境ではAPR_HAS_MMAPは定義されているので、この値は1が初期値。
この構造体の先頭の変数、refcountは、この情報が複数のバケットで共有されるタイプのリソースであることを意味している。
この変数には次の構造体 apr_bucket_refcount を使ってアクセスされる。
これを見ると、split関数とcopy関数に共有リソースの処理用関数がそのまま使われている。
また、file_bucket_destroy()からも、共有リソース破棄用の関数(apr_bucket_shared_destroy)が呼び出されている。
共有リソースというのは、参照するデータを複数のバケットで共有するための仕組みで、refcountはその名の通り、このリソースを参照しているバケット数を格納するようになっている。
初期値は1で、コピー(copy)や、分割(split)によって、その数はカウントアップされる。
apr_bucket_simple_copy(a,b)は、 apr_bucket *bにapr_bucketの領域を確保し、値を*aからコピーする処理だ。
この時、タイプ依存情報のdataのアドレスもコピーされるので、a,bは同じapr_bucket_file情報を参照するようになる。
そして、データ依存関数のrefcountをカウントアップする。
apr_bucket_simple_splitでは、内部でapr_bucket_simple_copy()を実行し、同じapr_bucketを複製する。
そして、バケット情報のstartとlengthを調整している。
pointがバケットaのサイズより大きければ、エラーで返している。
バケットaの複製バケットbを作成する。
バケットaの方のlengthをpointにおきかえる。
バケットbの方のlengthはpoint分、引き算する。
バケットbの先頭を元のaの先頭から、point分、先に進める。
そして、バケットbをbucket brigadeの バケットaの後ろに挿入する。
バケットaは、aの先頭から、pointまでのデータを含むようになり、
aから複製されたバケットbは、先頭がa+pointに、そして、もとの長さ(length)のpointから後ろ分を格納する。
新しいバケットbは、bucket brigadeのaの後ろに追加される。
apr_bucket_simple_split()でもタイプ異存情報dataは同じアドレスの情報を参照している。
そこでrefcountをカウントアップしている。
FILEデータタイプの場合、その場合に、実際のそのリソースを破棄する処理を行うようになっている。
この36行目 apr_bucket_shared_destroy(f)が、共有リソースのrefcountをマイナスする処理で、マイナスした結果が0だと真を返す。
refcountが0になると(返り値が真だと)、この関数では、apr_bucket_free()を実行するようになっている。
read関数は、次のようになっている
FILEデータバケットを読み込み、データのアドレスをstrにセットする。
lenは、読み込みデータ長が格納される。
読み込み時のブロックモードをblockに指定する。
file_make_mmapでは、FILEデータバケットをMMAPデータバケットに変換している。
FILEデータバケットの can_mmapが0の場合にはMMAPに変換できないが、
初期値では、ビルド環境依存になるが、mmapが利用可能(APR_HAS_MMAPが定義されている)なら、このフラグは1で初期化されている。
ちなみに、MMAPデータバケットには参照できるデータサイズの上限が決まっている。
APR_MMAP_LIMITバイト(MMAP_LIMITまたは、4*1024*1024(4MB))だ。
これを越えるファイルは珍しくないだろう。
この場合、FILEデータオブジェクトが、4MBまででsplitされ、先頭の4MBがMMAPデータバケットに変換される。
その後、MMAPデータバケットのread()関数を実行している。
ここ以降は、MMAPデータバケットに変換できない場合の処理だ。
今回は割愛する。
バケット dataを メモリプールreqpoolに確保しなおす処理だ。
現在使用されるメモリプールが短命な場合に不都合が生じる(バケットが使用される予定があるのに破棄されてしまう)ようなケースで、より長命なプール上に退避するために使用する。
apr_foo_pool_get()はgrepしても見つからない。
メモリプールは、aprライブラリが提供する機能で、親子(parent-child)関係と兄弟姉妹(sibling)関係を持っている。
apr_pool_create_ex()で引数にメモリプールが指定されると、生成されるプールの親にそのメモリプールがセットされ、親メモリプール側には子として新しいメモリプールがセットされる。
親メモリプールが複数の子メモリプールを持つ場合には、子メモリプールには兄弟関係を持つメモリプールのリストが接続される。
メモリプールが破棄される場合、そのメモリプールの子孫のプールもその兄弟も同時に破棄される。
reqpoolに確保しなおす要求の目的はより長命なプールにバケットを移すことであるから、reqpool自体が現在確保されているcurpoolの子孫であった場合には、確保しなおしても意味がないことになる。
apr_pool_is_ancestor()は、reqpoolのparentを辿って、curpoolが現れないかどうかをチェックする。
もし現れた場合(reqpoolがcurpoolの子孫の場合)、この関数では処理を行わない。
なお、reqpoolとcurpoolが同じプールである場合もこの関数は真を返す。
以降は、reqpoolがcurpoolの子孫でない場合の処理だ。
apr_bucket_file情報の内部に持っているメモリプール readpool も 親子関係をチェックする。
apr_file_t情報をreqpoolに退避する。
aprライブラリ関数で定義されている。
apr_bucket_file情報のapr_file_t情報を fdを退避先のfdに更新して、退避処理が終わる。
これは次のような構造体だ。
ファイルディスクリプタがセットされる。
FILEデータバケットがcopy()やsplit()で新たなapr_file_t情報を生成しては、内部で次々にファイルをオープンすることになりかねない
共有リソース化することでそういった事象が回避できる。
ファイル名
apr_file_open()で指定されたフラグが保持されている。
EnableSendfileが有効な場合には、APR_SENDFILE_ENABLEDフラグもONとなっている。
入出力をバッファモードで処理するかどうかの識別。
フラグにAPR_FOPEN_BUFFEREDか、APR_BUFFEREDを指定した場合に有効となる。
マルチスレッドでの排他制御用
この関数の引数には、apr_file_t情報が渡されている。
FILEデータバケットが内部で参照するデータ長には上限が設けられている。
1GBだ。
例えば、10GBのファイルの場合、1GB単位に分割されて、10個のFILEデータバケットが生成されている。
このとき、バケットの格納データ自体は同じ、apr_file_tで、バケット情報に持つstartとlengthが連続したデータを参照するように調整されることになる。
つまり、1つめのバケットはstart=0でlength=4GB、2つめのバケットは、startが4GBでlengthは同じく4GBと続き、10個のFILEバケットが生成される。
このapr_brigade_insert_file()関数の利用例を見てみる。
例えば、ハンドラフック関数であるdefault_handler()で使われている。
GET/POSTリクエストの処理の経路となる。
apr_file_open()で、r->filenameに指定されたファイルをオープンする。
この処理で生成されるのがapr_file_t情報で、fdに格納される。
メモリ領域は、r->poolを使用する。
この処理では、 (APR_READ|APR_BINARY) → (O_RDONLY|O_BINARY) に変換され、ファイルをオープンする際のフラグに使用される。
ファイル識別子は、apr_file_t情報のfiledes変数にセットされる。
また、open()では使用されないAP_SENDFILE_ENABLEDフラグを含め、フラグ情報は apr_file_t情報のflagsにセットされる。
初期値では、blockingはBLK_ON(ブロック有効)。
bufferedは、APR_FOPEN_BUFFEREDフラグの有無で判定されるが、ここでは指定されていないので、0。
bufferやbuffsizeはAPR_FOPEN_BUFFEREDが有効な場合に使用さえるが、ここでは使用されない。
その他の変数が初期化される。
そして、このapr_file_t情報のcleanup関数が登録される。
以下、apr_file_openのコードを抜粋引用しておく。
この処理が失敗した場合がこのif文内の経路だ。
エラーログを出力して、HTTP_FORBIDDENが戻り値になっている。
ファイルオープン後の処理に進む。
bucket brigade bbを作成する。
生成するのはrequestプール(r->pool)上になっている。
ここで、先に生成したapr_file_t情報 fdを指定して、FILEデータバケットを生成し、bucket brigade bbに追加する処理が行われる。
第3引数は対象ファイルのデータの先頭位置を指定している。0は先頭ということだ。
第4引数は、データサイズ。r->finfo.sizeには、ファイルサイズがセット済みだ。
この関数では、つまり、r->filenameで用意されたファイル全体をFILEデータバケットとして、bucket brigadeに追加している。
EOSメタデータバケットを作成し、さらにbucket brigade bbに追加する。
そして、この処理では、生成したbucket brigade bbを出力フィルタに引き渡している。
出力フィルタは、最終的にはCORE出力フィルタに引き渡され、AP_SENDFILE_ENABLED フラグが有効かどうかによって最終的な処理の方式は異なるが、このFILEデータバケットはソケットからクライアントにデータとして送信されることになる。
今回はここまで
(apr-util-1.5.4/include/apr_brigade.h) 612 struct apr_bucket_file { 613 /** Number of buckets using this memory */ 614 apr_bucket_refcount refcount;
共有リソース用の参照回数の情報
615 /** The file this bucket refers to */
616 apr_file_t *fd;
FILEバケット情報の処理対象のapr_file_t情報がセットされる
617 /** The pool into which any needed structures should
618 * be created while reading from this file bucket */
619 apr_pool_t *readpool;
620 #if APR_HAS_MMAP
621 /** Whether this bucket should be memory-mapped if
622 * a caller tries to read from it */
623 int can_mmap;
mmapが使用できるかどうかのフラグ。
centos環境ではAPR_HAS_MMAPは定義されているので、この値は1が初期値。
624 #endif /* APR_HAS_MMAP */ 625 };
この構造体の先頭の変数、refcountは、この情報が複数のバケットで共有されるタイプのリソースであることを意味している。
この変数には次の構造体 apr_bucket_refcount を使ってアクセスされる。
(include/apr_buckets.h) 524 typedef struct apr_bucket_refcount apr_bucket_refcount; 531 struct apr_bucket_refcount { 532 /** The number of references to this bucket */ 533 int refcount; 534 };
(2)FILEデータバケットタイプ情報
FILEデータバケットのバケットタイプ apr_bucket_type_t情報は次の通り。(apr-util-1.5.4/buckets/apr_buckets_file.c)
221 APU_DECLARE_DATA const apr_bucket_type_t apr_bucket_type_file = {
222 "FILE", 5, APR_BUCKET_DATA,
223 file_bucket_destroy,
224 file_bucket_read,
225 file_bucket_setaside,
226 apr_bucket_shared_split,
227 apr_bucket_shared_copy
228 };
これを見ると、split関数とcopy関数に共有リソースの処理用関数がそのまま使われている。
また、file_bucket_destroy()からも、共有リソース破棄用の関数(apr_bucket_shared_destroy)が呼び出されている。
共有リソースというのは、参照するデータを複数のバケットで共有するための仕組みで、refcountはその名の通り、このリソースを参照しているバケット数を格納するようになっている。
初期値は1で、コピー(copy)や、分割(split)によって、その数はカウントアップされる。
(2.1)copy関数
下記はcopy関数だ。apr-util-1.5.4/buckets/apr_buckets_refcount.c) 33 APU_DECLARE_NONSTD(apr_status_t) apr_bucket_shared_copy(apr_bucket *a, 34 apr_bucket **b) 35 { 36 apr_bucket_refcount *r = a->data; 37 38 apr_bucket_simple_copy(a, b);
apr_bucket_simple_copy(a,b)は、 apr_bucket *bにapr_bucketの領域を確保し、値を*aからコピーする処理だ。
(apr-util-1.5.4/buckets/apr_buckets_simple.c)
19 APU_DECLARE_NONSTD(apr_status_t) apr_bucket_simple_copy(apr_bucket *a,
20 apr_bucket **b)
21 {
22 *b = apr_bucket_alloc(sizeof(**b), a->list); /* XXX: check for failure? */
23 **b = *a;
24
25 return APR_SUCCESS;
26 }
この時、タイプ依存情報のdataのアドレスもコピーされるので、a,bは同じapr_bucket_file情報を参照するようになる。
39 r->refcount++;
そして、データ依存関数のrefcountをカウントアップする。
40 41 return APR_SUCCESS; 42 }
(2.2)split関数
続いて、split関数だ(apr-util-1.5.4/buckets/apr_buckets_refcount.c) 19 APU_DECLARE_NONSTD(apr_status_t) apr_bucket_shared_split(apr_bucket *a, 20 apr_size_t point) 21 { 22 apr_bucket_refcount *r = a->data; 23 apr_status_t rv; 24 25 if ((rv = apr_bucket_simple_split(a, point)) != APR_SUCCESS) {
apr_bucket_simple_splitでは、内部でapr_bucket_simple_copy()を実行し、同じapr_bucketを複製する。
そして、バケット情報のstartとlengthを調整している。
(apr-util-1.5.4/buckets/apr_buckets_simple.c)
28 APU_DECLARE_NONSTD(apr_status_t) apr_bucket_simple_split(apr_bucket *a,
29 apr_size_t point)
30 {
31 apr_bucket *b;
32
33 if (point > a->length) {
pointがバケットaのサイズより大きければ、エラーで返している。
34 return APR_EINVAL;
35 }
36
37 apr_bucket_simple_copy(a, &b);
バケットaの複製バケットbを作成する。
38 39 a->length = point;
バケットaの方のlengthをpointにおきかえる。
40 b->length -= point;
バケットbの方のlengthはpoint分、引き算する。
41 b->start += point;
バケットbの先頭を元のaの先頭から、point分、先に進める。
42
43 APR_BUCKET_INSERT_AFTER(a, b);
そして、バケットbをbucket brigadeの バケットaの後ろに挿入する。
44 45 return APR_SUCCESS; 46 }
バケットaは、aの先頭から、pointまでのデータを含むようになり、
aから複製されたバケットbは、先頭がa+pointに、そして、もとの長さ(length)のpointから後ろ分を格納する。
新しいバケットbは、bucket brigadeのaの後ろに追加される。
26 return rv; 27 } 28 r->refcount++;
apr_bucket_simple_split()でもタイプ異存情報dataは同じアドレスの情報を参照している。
そこでrefcountをカウントアップしている。
29 30 return APR_SUCCESS; 31 }
(2.3)destroy関数
バケットを破棄(destroy)するときにマイナスされていく。refcount==0となると、このリソースを参照しているバケットがないことになる。FILEデータタイプの場合、その場合に、実際のそのリソースを破棄する処理を行うようになっている。
(apr-util-1.5.4/buckets/apr_buckets_file.c) 32 static void file_bucket_destroy(void *data) 33 { 34 apr_bucket_file *f = data; 35 36 if (apr_bucket_shared_destroy(f)) { 37 /* no need to close the file here; it will get 38 * done automatically when the pool gets cleaned up */ 39 apr_bucket_free(f); 40 } 41 }
この36行目 apr_bucket_shared_destroy(f)が、共有リソースのrefcountをマイナスする処理で、マイナスした結果が0だと真を返す。
refcountが0になると(返り値が真だと)、この関数では、apr_bucket_free()を実行するようになっている。
(2.4)read関数
read関数は、次のようになっている
(apr-util-1.5.4/buckets/apr_buckets_file.c)
75 static apr_status_t file_bucket_read(apr_bucket *e, const char **str,
76 apr_size_t *len, apr_read_type_e block)
FILEデータバケットを読み込み、データのアドレスをstrにセットする。
lenは、読み込みデータ長が格納される。
読み込み時のブロックモードをblockに指定する。
77 {
78 apr_bucket_file *a = e->data;
79 apr_file_t *f = a->fd;
80 apr_bucket *b = NULL;
81 char *buf;
82 apr_status_t rv;
83 apr_size_t filelength = e->length; /* bytes remaining in file past offset */
84 apr_off_t fileoffset = e->start;
86 apr_int32_t flags;
88
90 if (file_make_mmap(e, filelength, fileoffset, a->readpool)) {
file_make_mmapでは、FILEデータバケットをMMAPデータバケットに変換している。
FILEデータバケットの can_mmapが0の場合にはMMAPに変換できないが、
初期値では、ビルド環境依存になるが、mmapが利用可能(APR_HAS_MMAPが定義されている)なら、このフラグは1で初期化されている。
ちなみに、MMAPデータバケットには参照できるデータサイズの上限が決まっている。
APR_MMAP_LIMITバイト(MMAP_LIMITまたは、4*1024*1024(4MB))だ。
これを越えるファイルは珍しくないだろう。
この場合、FILEデータオブジェクトが、4MBまででsplitされ、先頭の4MBがMMAPデータバケットに変換される。
91 return apr_bucket_read(e, str, len, block);
その後、MMAPデータバケットのread()関数を実行している。
92 }
ここ以降は、MMAPデータバケットに変換できない場合の処理だ。
今回は割愛する。
: 154 }
(2.5)setaside関数
setaside関数は次のようになっている。(apr-util-1.5.4/buckets/apr_buckets_file.c)
201 static apr_status_t file_bucket_setaside(apr_bucket *data, apr_pool_t *reqpool)
バケット dataを メモリプールreqpoolに確保しなおす処理だ。
現在使用されるメモリプールが短命な場合に不都合が生じる(バケットが使用される予定があるのに破棄されてしまう)ようなケースで、より長命なプール上に退避するために使用する。
202 {
203 apr_bucket_file *a = data->data;
204 apr_file_t *fd = NULL;
205 apr_file_t *f = a->fd;
206 apr_pool_t *curpool = apr_file_pool_get(f);
apr_foo_pool_get()はgrepしても見つからない。
・ APR_POOL_DECLARE_ACCESSOR(foo)マクロ
および
・ APR_POOL_IMPLEMENT_ACCESSOR(foo)マクロ
で定義されており、apr_foo_t構造体のpoolを返す。および
・ APR_POOL_IMPLEMENT_ACCESSOR(foo)マクロ
207
208 if (apr_pool_is_ancestor(curpool, reqpool)) {
メモリプールは、aprライブラリが提供する機能で、親子(parent-child)関係と兄弟姉妹(sibling)関係を持っている。
apr_pool_create_ex()で引数にメモリプールが指定されると、生成されるプールの親にそのメモリプールがセットされ、親メモリプール側には子として新しいメモリプールがセットされる。
親メモリプールが複数の子メモリプールを持つ場合には、子メモリプールには兄弟関係を持つメモリプールのリストが接続される。
メモリプールが破棄される場合、そのメモリプールの子孫のプールもその兄弟も同時に破棄される。
reqpoolに確保しなおす要求の目的はより長命なプールにバケットを移すことであるから、reqpool自体が現在確保されているcurpoolの子孫であった場合には、確保しなおしても意味がないことになる。
apr_pool_is_ancestor()は、reqpoolのparentを辿って、curpoolが現れないかどうかをチェックする。
もし現れた場合(reqpoolがcurpoolの子孫の場合)、この関数では処理を行わない。
なお、reqpoolとcurpoolが同じプールである場合もこの関数は真を返す。
209 return APR_SUCCESS; 210 } 211
以降は、reqpoolがcurpoolの子孫でない場合の処理だ。
212 if (!apr_pool_is_ancestor(a->readpool, reqpool)) {
apr_bucket_file情報の内部に持っているメモリプール readpool も 親子関係をチェックする。
213 a->readpool = reqpool;
214 }
215
216 apr_file_setaside(&fd, f, reqpool);
apr_file_t情報をreqpoolに退避する。
aprライブラリ関数で定義されている。
217 a->fd = fd;
apr_bucket_file情報のapr_file_t情報を fdを退避先のfdに更新して、退避処理が終わる。
218 return APR_SUCCESS; 219 }
(3)apr_file_t情報
apr_bucket_file情報のファイル情報は、apr_file_t情報が持っている。これは次のような構造体だ。
(apr-1.5.1/include/arch/unix/apr_arch_file_io.h) 93 struct apr_file_t { 94 apr_pool_t *pool; 95 int filedes;
ファイルディスクリプタがセットされる。
FILEデータバケットがcopy()やsplit()で新たなapr_file_t情報を生成しては、内部で次々にファイルをオープンすることになりかねない
共有リソース化することでそういった事象が回避できる。
96 char *fname;
ファイル名
97 apr_int32_t flags;
apr_file_open()で指定されたフラグが保持されている。
EnableSendfileが有効な場合には、APR_SENDFILE_ENABLEDフラグもONとなっている。
98 int eof_hit;
99 int is_pipe;
100 apr_interval_time_t timeout;
101 int buffered;
入出力をバッファモードで処理するかどうかの識別。
フラグにAPR_FOPEN_BUFFEREDか、APR_BUFFEREDを指定した場合に有効となる。
102 enum {BLK_UNKNOWN, BLK_OFF, BLK_ON } blocking;
103 int ungetchar; /* Last char provided by an unget op. (-1 = no char)*/
105 /* if there is a timeout set, then this pollset is used */
106 apr_pollset_t *pollset;
108 /* Stuff for buffered mode */
109 char *buffer;
110 apr_size_t bufpos; /* Read/Write position in buffer */
111 apr_size_t bufsize; /* The size of the buffer */
112 unsigned long dataRead; /* amount of valid data read into buffer */
113 int direction; /* buffer being used for 0 = read, 1 = write */
114 apr_off_t filePtr; /* position in file of handle */
116 struct apr_thread_mutex_t *thlock;
マルチスレッドでの排他制御用
118 };
(4)apr_file_t情報のセットアップ : default_handler
FILEデータバケットは、apr_brigade_insert_file()で作成される。(apr-util-1.5.4/buckets/apr_brigade.c)
707 APU_DECLARE(apr_bucket *) apr_brigade_insert_file(apr_bucket_brigade *bb,
708 apr_file_t *f,
709 apr_off_t start,
710 apr_off_t length,
711 apr_pool_t *p)
この関数の引数には、apr_file_t情報が渡されている。
apr_bucket_brigade *bb | 生成されたFILEデータバケットはこのbucket brigade bbの末尾に追加される。 |
apr_file_t *f | 生成するFILEデータバケットに格納するapr_file_t情報 |
apr_off_t start | 生成するFILEデータバケットは、対象のapr_file_t情報のatartバイト目からを参照する。 |
apr_off_t length | 生成するFILEデータバケットは、対象のapr_file_t情報のstartバイト目から、lengthバイト分を参照する。 |
apr_pool_t *p | 処理で使用するメモリプール |
FILEデータバケットが内部で参照するデータ長には上限が設けられている。
1GBだ。
(apr-util-1.5.4/buckets/apr_brigade.c) 704 /* A "safe" maximum bucket size, 1Gb */ 705 #define MAX_BUCKET_SIZE (0x40000000)
例えば、10GBのファイルの場合、1GB単位に分割されて、10個のFILEデータバケットが生成されている。
このとき、バケットの格納データ自体は同じ、apr_file_tで、バケット情報に持つstartとlengthが連続したデータを参照するように調整されることになる。
つまり、1つめのバケットはstart=0でlength=4GB、2つめのバケットは、startが4GBでlengthは同じく4GBと続き、10個のFILEバケットが生成される。
このapr_brigade_insert_file()関数の利用例を見てみる。
例えば、ハンドラフック関数であるdefault_handler()で使われている。
(httpd-2.4.9/server/core.c)
4248 static int default_handler(request_rec *r)
4249 {
:
4281 if (r->method_number == M_GET || r->method_number == M_POST) {
GET/POSTリクエストの処理の経路となる。
:
4330 if ((status = apr_file_open(&fd, r->filename, APR_READ | APR_BINARY
4332 | AP_SENDFILE_ENABLED(d->enable_sendfile)
4334 , 0, r->pool)) != APR_SUCCESS) {
apr_file_open()で、r->filenameに指定されたファイルをオープンする。
この処理で生成されるのがapr_file_t情報で、fdに格納される。
メモリ領域は、r->poolを使用する。
この処理では、 (APR_READ|APR_BINARY) → (O_RDONLY|O_BINARY) に変換され、ファイルをオープンする際のフラグに使用される。
ファイル識別子は、apr_file_t情報のfiledes変数にセットされる。
また、open()では使用されないAP_SENDFILE_ENABLEDフラグを含め、フラグ情報は apr_file_t情報のflagsにセットされる。
初期値では、blockingはBLK_ON(ブロック有効)。
bufferedは、APR_FOPEN_BUFFEREDフラグの有無で判定されるが、ここでは指定されていないので、0。
bufferやbuffsizeはAPR_FOPEN_BUFFEREDが有効な場合に使用さえるが、ここでは使用されない。
その他の変数が初期化される。
そして、このapr_file_t情報のcleanup関数が登録される。
以下、apr_file_openのコードを抜粋引用しておく。
(apr-1.5.1/file_io/unix/open.c)
90 APR_DECLARE(apr_status_t) apr_file_open(apr_file_t **new,
91 const char *fname,
92 apr_int32_t flag,
93 apr_fileperms_t perm,
94 apr_pool_t *pool)
95 {
96 apr_os_file_t fd;
97 int oflags = 0;
99 apr_thread_mutex_t *thlock;
100 apr_status_t rv;
102
103 if ((flag & APR_FOPEN_READ) && (flag & APR_FOPEN_WRITE)) {
104 oflags = O_RDWR;
105 }
106 else if (flag & APR_FOPEN_READ) {
107 oflags = O_RDONLY;
108 }
109 else if (flag & APR_FOPEN_WRITE) {
110 oflags = O_WRONLY;
111 }
112 else {
113 return APR_EACCES;
114 }
:
133 if (flag & APR_FOPEN_BINARY) {
134 oflags |= O_BINARY;
135 }
:
171
172 if (perm == APR_OS_DEFAULT) {
173 fd = open(fname, oflags, 0666);
174 }
175 else {
176 fd = open(fname, oflags, apr_unix_perms2mode(perm));
177 }
178 if (fd < 0) {
179 return errno;
180 }
181 if (!(flag & APR_FOPEN_NOCLEANUP)) {
183 static int has_o_cloexec = 0;
184 if (!has_o_cloexec)
186 {
187 int flags;
188
189 if ((flags = fcntl(fd, F_GETFD)) == -1) {
190 close(fd);
191 return errno;
192 }
193 if ((flags & FD_CLOEXEC) == 0) {
194 flags |= FD_CLOEXEC;
195 if (fcntl(fd, F_SETFD, flags) == -1) {
196 close(fd);
197 return errno;
198 }
199 }
201 else {
202 has_o_cloexec = 1;
203 }
205 }
206 }
207
208 (*new) = (apr_file_t *)apr_pcalloc(pool, sizeof(apr_file_t));
209 (*new)->pool = pool;
210 (*new)->flags = flag;
211 (*new)->filedes = fd;
212
213 (*new)->fname = apr_pstrdup(pool, fname);
214
215 (*new)->blocking = BLK_ON;
216 (*new)->buffered = (flag & APR_FOPEN_BUFFERED) > 0;
217
218 if ((*new)->buffered) {
219 (*new)->buffer = apr_palloc(pool, APR_FILE_DEFAULT_BUFSIZE);
220 (*new)->bufsize = APR_FILE_DEFAULT_BUFSIZE;
222 if ((*new)->flags & APR_FOPEN_XTHREAD) {
223 (*new)->thlock = thlock;
224 }
226 }
227 else {
228 (*new)->buffer = NULL;
229 }
230
231 (*new)->is_pipe = 0;
232 (*new)->timeout = -1;
233 (*new)->ungetchar = -1;
234 (*new)->eof_hit = 0;
235 (*new)->filePtr = 0;
236 (*new)->bufpos = 0;
237 (*new)->dataRead = 0;
238 (*new)->direction = 0;
240 /* Start out with no pollset. apr_wait_for_io_or_timeout() will
241 * initialize the pollset if needed.
242 */
243 (*new)->pollset = NULL;
245 if (!(flag & APR_FOPEN_NOCLEANUP)) {
246 apr_pool_cleanup_register((*new)->pool, (void *)(*new),
247 apr_unix_file_cleanup,
248 apr_unix_child_file_cleanup);
249 }
250 return APR_SUCCESS;
251 }
この処理が失敗した場合がこのif文内の経路だ。
エラーログを出力して、HTTP_FORBIDDENが戻り値になっている。
4335 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(00132) 4336 "file permissions deny server access: %s", r->filename); 4337 return HTTP_FORBIDDEN; 4338 } :
ファイルオープン後の処理に進む。
4350 bb = apr_brigade_create(r->pool, c->bucket_alloc);
bucket brigade bbを作成する。
生成するのはrequestプール(r->pool)上になっている。
4351
4352 if ((errstatus = ap_meets_conditions(r)) != OK) {
4353 apr_file_close(fd);
4354 r->status = errstatus;
4355 }
4356 else {
4357 e = apr_brigade_insert_file(bb, fd, 0, r->finfo.size, r->pool);
ここで、先に生成したapr_file_t情報 fdを指定して、FILEデータバケットを生成し、bucket brigade bbに追加する処理が行われる。
第3引数は対象ファイルのデータの先頭位置を指定している。0は先頭ということだ。
第4引数は、データサイズ。r->finfo.sizeには、ファイルサイズがセット済みだ。
この関数では、つまり、r->filenameで用意されたファイル全体をFILEデータバケットとして、bucket brigadeに追加している。
4358 4359 #if APR_HAS_MMAP 4360 if (d->enable_mmap == ENABLE_MMAP_OFF) { 4361 (void)apr_bucket_file_enable_mmap(e, 0); 4362 } 4363 #endif 4364 } 4365 4366 e = apr_bucket_eos_create(c->bucket_alloc); 4367 APR_BRIGADE_INSERT_TAIL(bb, e);
EOSメタデータバケットを作成し、さらにbucket brigade bbに追加する。
4368
4369 status = ap_pass_brigade(r->output_filters, bb);
そして、この処理では、生成したbucket brigade bbを出力フィルタに引き渡している。
出力フィルタは、最終的にはCORE出力フィルタに引き渡され、AP_SENDFILE_ENABLED フラグが有効かどうかによって最終的な処理の方式は異なるが、このFILEデータバケットはソケットからクライアントにデータとして送信されることになる。
:
今回はここまで