由于ODU命令比较多,特别是关键的unload命令比较复杂,本文将简单介绍几种场景下使用ODU进行数据恢复时,使用的命令序列。本文不会详细介绍每个命令的使用,详细的命令请参考本网站ODU页面下的链接

场景1. 数据库不能启动,但是SYSTEM表空间中的数据字典是完整的。

  • 生成数据字典:unload dict
  • 列出用户: list user
  • 列出用户下的所有表: list table username
  • 恢复表: unload table username.tablename
  • 也可以按用户恢复: unload user username

场景2. 表被TRUNCATE。

  • OFFLINE表所在的表空间
  • 生成数据字典:unload dict
  • 显示表的段头:desc username.tablename
  • 找到实际的data object id: dump datafile file# block block#
  • 扫描数据:scan extent
  • 恢复表:unload table username.tablename object object_id

UPDATE:
从3.0.7版本开始,恢复Truncate表更方便,只需要执行下面的步骤:

  • OFFLINE表所在的表空间
  • 生成数据字典:unload dict
  • 扫描数据:scan extent
  • 恢复表:unload table username.tablename object auto

场景3. 表被DROP。

  • OFFLINE表所在的表空间
  • 使用logminer从日志里面挖掘被drop掉的表其data object id,如果不能挖掘,按下面的场景4进行恢复。
  • 扫描数据:scan extent
  • 如果没有表结构信息,需要自动来判断:unload object data_object_id sample
  • 恢复表:unload object data_object_id column coltype coltype...

场景4. 系统表空间损坏。

  • 扫描数据:scan extent
  • 搜索数据:unload object all sample
  • 从结果文件sample.txt查找需要的数据
  • 恢复需要的表:unload object data_object_id column coltype coltype...

场景5. 表中数据被DELETE。

  • 将参数unload_deleted设置为YES
  • 生成数据字典:unload dict
  • 恢复表: unload table username.tablename
,

本文将以ODU 3.0.2版的配置文件为例,详细介绍ODU的配置参数,从配置文件也可以体会到ODU的众多功能特点。

ODU默认的配置文件是config.txt,在启动ODU时,会自动打开这个配置文件,当然在进入ODU后,仍然可以通过"load config [config filename]"命令来装入配置文件,这个命令中的config filename(配置文件名)是可选的,如果省略此项,将载入默认的配置文件config.txt。

配置文件是一个纯文本文件,每行为一个配置参数,为“参数名”和“值”,二者均不区分大小写,之间以若干空格分隔。下面是一个配置文件的内容示例:

byte_order   little
block_size    8192
data_path   data
lob_path    /lob
charset_name ZHS16GBK
ncharset_name AL16UTF16
output_format text
lob_storage   infile
clob_byte_order  little
delimiter  |
unload_deleted no

下面将详细介绍每一项配置参数的用法以及作用:

1. BYTE_ORDER

这个参数,指示数据文件平台字节序。可选值为"LITTLE"和"BIG",默认值为“LITTLE”。

这个参数跟ODU运行在哪个平台无关,而只与数据文件的平台有关。比如,数据文件是AIX平台上的,那么这个值就是“BIG",而数据文件是x86平台上的,那么这个值就是”LITTLE“。ODU能够跨平台恢复数据,比如可以将AIX上的Oracle数据文件复制到Windows上,由Windows版本上的ODU进行恢复,反之亦然。只要通过BYTE_ORDER这个参数正确地指示数据文件的平台,即可实现跨平台恢复。

2. BLOCK_SIZE

这个参数设置数据文件缺省的块大小。ODU支持同一数据库具有不同块大小数据文件,如果在ODU的control文件中没有没文件指定块大小(参见《ODU命令详解 PartI》中“open”命令),则使用配置文件中的BLOCK_SIZE指定的大小。这个参数可选的值有2048、4096、8192、16384、32768,默认值是8192。

3. DATA_PATH

DATA_PATH指定恢复的数据所存储的目录,如果需要恢复的数量量非常大,可以用这个参数值指定一个与ODU软件所在目录不同的路径。注意这个参数指定的目录必须是已经存在的,ODU不会自动创建这个目录。
可以使用相对路径,也可以使用绝对路径。默认值为”data“,表示恢复的数据默认放在ODU软件所在目录的data子目录中。

4. LOB_PATH

Read the rest of this entry

,

虽然10g及以上版本的Oracle数据库,提供了recyclebin(回收站)功能,可以找回被drop的表。但是仍然存在着很多8i、9i的库以及没有开启recyclebin功能、drop时直接purge操作等,这样的情况下,如果想找回被意外drop的表,常规的手段是通过备份来恢复。如果没有备份,那就没有办法来恢复了。不过ODU提供了一个可能,在没有备份的情况下,恢复被drop表的数据。

下面通过一个示例来演示如何使用ODU来恢复被drop的表。
首先创建一个测试表:

SQL> create table odu_test ( a number,b varchar2(10),c nvarchar2(30),d varchar2(20),e date,f timestamp,g binary_float,h binary_double);

Table created.

SQL> insert into odu_test select rownum,lpad('x',10),'NC测试' || rownum, 'ZHS测试'|| rownum,sysdate+dbms_random.value(0,100),systimestamp+dbms_random.value(0,100),rownum+dbms_random.value(0,10000),rownum+dbms_random.value(0,10000) from dba_objects where rownum<=10000;

10000 rows created.

SQL> commit;

Commit complete.

SQL> create table t1 as select * from odu_test;

Table created.
SQL> drop table odu_test purge;

Table dropped.

在发现重要的表被意外drop掉的时候,应该立即停止应用,offline那个表所在的表空间或关闭数据库。这里odu_test表是建在users表空间下,先将users表空间offline:

SQL> alter tablespace users offline;

Tablespace altered.

然后需要使用logminer来查找被drop表的data object id:

SQL> select group#,status from v$log;

    GROUP# STATUS
---------- ----------------
         1 INACTIVE
         2 INACTIVE
         3 CURRENT

SQL> col member for a50
SQL> select member from v$logfile where group#=3;

MEMBER
--------------------------------------------------
/u01/app/oracle/oradata/xty/redo03.log

SQL> exec sys.dbms_logmnr.add_logfile(logfilename=>'/u01/app/oracle/oradata/xty/redo03.log');

PL/SQL procedure successfully completed.

SQL> exec sys.dbms_logmnr.start_logmnr(options=>sys.dbms_logmnr.dict_from_online_catalog);

PL/SQL procedure successfully completed.
SQL> select scn,timestamp,sql_redo from v$logmnr_contents where operation='DDL' and sql_redo like '%odu_test%' order by 2 ;

SCN TIMESTAMP SQL_REDO
---------- ------------------- ----------------------------------------------------------------------
    681455 2009-05-08 11:20:50 create table odu_test ( a number,b varchar2(10),c nvarchar2(30),d varc
                               har2(20),e date,f timestamp,g binary_float,h binary_double);

    681521 2009-05-08 11:21:17 create table t1 as select * from odu_test;
    681567 2009-05-08 11:21:34 drop table odu_test purge;

SQL> select scn,timestamp,sql_redo from v$logmnr_contents where timestamp=to_date('2009-05-08 11:21:34','yyyy-mm-dd hh24:mi:ss') order by 1;

       SCN  SQL_REDO
----------  ----------------------------------------------------------------------
    681566  set transaction read write;
    681567  drop table odu_test purge;
    681569  Unsupported
    681570  Unsupported
    681570 
    681570 
    681570 
    681570  Unsupported
    681570 
    681570 
    681570 
    681570  Unsupported
    681570 
    681570 
    681570 
    681570  Unsupported
    681570 
    681570 
    681570 
    681570 
    681570 
    681570 
    681570  Unsupported
    681570  Unsupported
    681570 
    681570 
    681570 
    681570  Unsupported
    681570 
    681570 
    681570 
    681570  Unsupported
    681570 
    681570 
    681570 
    681571  Unsupported
    681572 
    681572  delete from "SYS"."OBJ$" where "OBJ#" = '52230' and "DATAOBJ#" = '5223
            0' and "OWNER#" = '57' and "NAME" = 'ODU_TEST' and "NAMESPACE" = '1' a
            nd "SUBNAME" IS NULL and "TYPE#" = '2' and "CTIME" = TO_DATE('2009-05-
            08 11:20:46', 'yyyy-mm-dd hh24:mi:ss') and "MTIME" = TO_DATE('2009-05-
            08 11:20:46', 'yyyy-mm-dd hh24:mi:ss') and "STIME" = TO_DATE('2009-05-
            08 11:20:46', 'yyyy-mm-dd hh24:mi:ss') and "STATUS" = '1' and "REMOTEO
            WNER" IS NULL and "LINKNAME" IS NULL and "FLAGS" = '0' and "OID$" IS N
            ULL and "SPARE1" = '6' and "SPARE2" = '1' and "SPARE3" IS NULL and "SP
            ARE4" IS NULL and "SPARE5" IS NULL and "SPARE6" IS NULL and ROWID = 'A
            AAAASAABAAAMzdAAS';

    681572 
    681573  commit;
    681574  set transaction read write;
    681574  Unsupported
    681576  commit;
    681577  set transaction read write;
    681579  Unsupported
    681581  commit;

SQL> exec sys.dbms_logmnr.end_logmnr;

PL/SQL procedure successfully completed.

从SCN为681572的几行中,delete from ”SYS”.”OBJ$” where ”OBJ#” = ’52230′ and ”DATAOBJ#” = ’52230′ 可以看到被drop表的data object id为52230。

下面我们使用ODU来恢复这个被删除的表:

Read the rest of this entry

,

意外Truncate表的事情时有发生,ODU提供了方便的恢复Truncate表的功能。被Truncate的表,只要原来的空间没有被重用(即数据被覆盖),则数据都是可以恢复的。

如果发现一个表被意外地Truncate,而需要马上恢复。首先要做的就是关闭数据库,或者OFFLINE那个表所在的表空间,或者关闭所有应用。目的只有一个,确保空间不会被重用,数据不会被覆盖。

下面举例说明如何用ODU恢复被Truncate掉的表。

1. 建立两个测试的表T1和T2,这两个表的数据完全一样。建两个数据完全一样的表的目的在于方便在恢复后对比数据。

SQL> connect test/test
已连接。

SQL> create table t1 as select * from dba_objects;

表已创建。

SQL> create table t2 as select * from t1;

表已创建。

SQL> truncate table t1;

表已截掉。

2. 我们OFFLINE掉T1表的表空间(实际上在实际的系统中,如果有比较多的活动,则表空间不容易被OFFLINE下来)。然后做一个Checkpoint,让ODU能够读到最新的数据字典数据。

SQL> select tablespace_name from user_tables where table_name='T1';

TABLESPACE_NAME
------------------------------
TEST

SQL> alter tablespace test offline;

表空间已更改。
SQL> alter system checkpoint;

系统已更改。

3. 运行ODU,并unload数据字典。

ODU> unload dict
get_bootstrap_dba: compat header size:12
CLUSTER C_USER# file_no: 1 block_no: 177
TABLE OBJ$ file_no: 1 block_no: 241
CLUSTER C_OBJ# file_no: 1 block_no: 49
CLUSTER C_OBJ# file_no: 1 block_no: 49
found IND$'s obj# 19
found IND$'s dataobj#:2,ts#:0,file#:1,block#:49,tab#:3
found TABPART$'s obj# 230
found TABPART$'s dataobj#:230,ts#:0,file#:1,block#:3313,tab#:0
found INDPART$'s obj# 234
found INDPART$'s dataobj#:234,ts#:0,file#:1,block#:3377,tab#:0
found TABSUBPART$'s obj# 240
found TABSUBPART$'s dataobj#:240,ts#:0,file#:1,block#:3473,tab#:0
found INDSUBPART$'s obj# 245
found INDSUBPART$'s dataobj#:245,ts#:0,file#:1,block#:3553,tab#:0
found IND$'s obj# 19
found IND$'s dataobj#:2,ts#:0,file#:1,block#:49,tab#:3
found LOB$'s obj# 156
found LOB$'s dataobj#:2,ts#:0,file#:1,block#:49,tab#:6
found LOBFRAG$'s obj# 258
found LOBFRAG$'s dataobj#:258,ts#:0,file#:1,block#:3761,tab#:0

Read the rest of this entry

,

接上文《ODU命令详解 PartII》,本文继续介绍ODU的命令

5.scan 命令

scan命令用于扫描数据文件中的segment以及extent。主要作用在于没有SYSTEM表空间时的数据恢复,以及TRUNCATE表和DROP表之后的数据恢复。命令格式如下:

scan extent [tablespace <ts#> [datafile <rfile#>] ] [object <data_object_id>]

扫描时可以指定表空间(只能指定表空间号),表空间中的1个文件;也可以扫描指定的data_object_id。扫描完成后,将在ODU的运行目录下生成ext.odu文件以及segment.txt文件,后者包含了扫描所找到的段头。

下面是一个示例:

ODU> scan extent tablespace 11

scanning extent...
scanning extent finished.

在恢复没有SYSTEM表空间和DROP,TRUNCATE表之前,需要运行scan命令。

6.dump 命令

dump命令模仿oracle的"alter system dump datafile"命令,解析并显示oracle的块格式。目前支持的块包括文件头(只有部分信息)、数据段头、表和索引数据块,其他的块只显示块头。这已经足够大部分情况的使用。ODU的升级版将解析并显示UNDO的段头和数据块。

dump命令用于帮助分析块格式,在ORACLE不能启动时(比如在关键的数据字典对象上有物理或逻辑坏块),用于分析块。dump命令的格式如下:

dump datafile <file#> block <block#>

这个命令格式与oracle的"alter system dump datafile“类似,这里的文件号是绝对文件号。
下面是几个示例输出:

Read the rest of this entry

,

接上文《ODU命令详解 PartI》,本文继续介绍ODU的命令

4.unload 命令

unload命令是ODU中命令格式最复杂的命令,但也是最关键的命令。命令的格式如下:

unload dict
unload table <schema.tablename> [partition <partition_name>]
unload table <schema.tablename> object <data_obj_id> [tablespace <ts_no>]
unload object <data_obj_id> [cluster <cluster_no>] [tablespace <ts_no>] column <type[,type,type......]>
    type: VARCHAR2 VARCHAR CHAR NUMBER SKIP LONG RAW                                                   
          DATE LONGRAW TIMESTAMP TIMESTAMPTZ TIMESTAMPLTZ                                              
          BINARY_FLOAT BINARY_DOUBLE  
unload user <schema name>

1) unload dict

unload dict用于从SYSTEM表空间解析数据字典,并将ODU所需要的数据字典数据保存到user.odu,tab.odu,obj.odu,col.odu,ind.odu等文件中。这样,ODU就可以利用数据字典信息UNLOAD数据。对于在有数据字典的情况下,ODU的功能能够得到最大程度地发挥:全面支持IOT表,全面支持LOB类型

ODU> unload dict
get_bootstrap_dba: compat header size:12
CLUSTER C_USER# file_no: 1 block_no: 177
TABLE OBJ$ file_no: 1 block_no: 241
CLUSTER C_OBJ# file_no: 1 block_no: 49
CLUSTER C_OBJ# file_no: 1 block_no: 49
found IND$'s obj# 19
found IND$'s dataobj#:2,ts#:0,file#:1,block#:49,tab#:3
found TABPART$'s obj# 230
found TABPART$'s dataobj#:230,ts#:0,file#:1,block#:3313,tab#:0
found INDPART$'s obj# 234
found INDPART$'s dataobj#:234,ts#:0,file#:1,block#:3377,tab#:0
found TABSUBPART$'s obj# 240
found TABSUBPART$'s dataobj#:240,ts#:0,file#:1,block#:3473,tab#:0
found INDSUBPART$'s obj# 245
found INDSUBPART$'s dataobj#:245,ts#:0,file#:1,block#:3553,tab#:0
found IND$'s obj# 19
found IND$'s dataobj#:2,ts#:0,file#:1,block#:49,tab#:3
found LOB$'s obj# 156
found LOB$'s dataobj#:2,ts#:0,file#:1,block#:49,tab#:6
found LOBFRAG$'s obj# 258
found LOBFRAG$'s dataobj#:258,ts#:0,file#:1,block#:3761,tab#:0

值得注意的是,ODU并不是从文件号为1的数据文件中得到bootstrap地址进而得到数据字典,而是从control.txt的第一行中得到bootstrap地址。所以,需要将SYSTEM表空间中的第1个数据文件放置于control.txt中的第1行。否则会得到如下的错误:

can not get bootstrap$ address from SYSTEM tablespace

得到数据字典后,你可以使用list user, list table等命令查看数据库中的用户及用户下的表、视图等对象。

2) unload table <schema .tablename> [partition <partition_name>]

这个命令格式用于导出某个用户下的表,如果指定了partition,则只导出分区表中的那个分区。例如:

ODU> unload table sys.t1

Unloading table: T1,object ID: 31417
Unloading segment,storage(Obj#=31417 DataObj#=31418 TS#=0 File#=1 Block#=83041 Cluster=0)

导出表时,会显示表名,对象ID(Object ID),以及导出段的段头等信息。
使用此命令,表名要使用表的全限定名,则“用户名”+“表名“。

3) unload table <schema.tablename> object <data_obj_id> [tablespace <ts_no>]

此命令格式与上一命令格式的差别在于,在导出的表名之后指定了数据对象ID(data object id),这用于导出表其实际data object id与数据字典中表的data object id不一致时的表数据。简单点说,就是用于导出被truncate的表数据。导出前需要用SCAN命令扫描数据文件,导出truncate表的数据的详细操作,将另行撰文说明。

4) unload object <data_obj_id> [cluster <cluster_no>] [tablespace <ts_no>] column <type[,type,type......]>

这条命令简单点说,就是用于没有SYSTEM表空间或者没有数据字典时的数据导出,或用于导出被DROP了的表。

这条命令中,列类型为以下:

VARCHAR2 VARCHAR CHAR NUMBER SKIP LONG RAW
DATE LONGRAW TIMESTAMP TIMESTAMPTZ TIMESTAMPLTZ
BINARY_FLOAT BINARY_DOUBLE

SKIP表示不导出那个列。目前不支持没有数据字典(和SYSTEM表空间)时的IOT表和LOB类型的列。导出前需要用SCAN命令扫描数据文件。

此条命令和上条命令中,为啥可以指定TABLESPACE,这是因为在一个数据库中,data object id可能不是唯一的。比如,使用了表空间传输就可能会有这样的情况。

后面将专门有文章描述如何在没有数据字典时导出数据。这是一个比较复杂的过程,所以此处只是介绍一下命令,不再详述。

5) unload user <schema name>

此命令格式用于导出一个用户下的所有表。此命令在有数据字典时才支持。这样简化了导出一个用户下所有的表的操作。

,

本文将介绍ODU支持的命令,以v2.3.0为基础详细描述ODU的命令。

odu的命令以回车为结束,而不是像SQLPLUS那样的使用“;"号

1. help命令

help命令,显示ODU支持的命令列表,每个命令后面有简短描述:

ODU> help

help      ----   get command list
exit      ----   exit from odu
spool     ----   spool information to file
host      ----   enter os terminal
load config ----   load config information from file
open      ----   load database filename list from file
osdump    ----   dump file format hex
dump      ----   dump oracle datafile block
rowid     ----   decode rowid components
unload    ----   unload data
scan      ----   scan extent or segment
list      ----   list schema object,partition,datafile
charset   ----   get or list supported charset name

在执行一个命令时,如果输入的命令格式不对,将会提示那个命令的命令格式。

2. load config命令
load config命令用于载入ODU的配置文件。命令格式如下:

load config [filename]

缺省的文件名是config.txt
ODU在启动时,会自动载入名为config.txt的配置文件。我将撰文专门介绍ODU的配置文件。
ODU启动后,也可以重新载入配置文件,载入新配置后,会立即生效。

下面是一个示例:

ODU> load config
byte_order little
block_size 8192
db_timezone -7
client_timezone 8
data_path data
lob_path lob
charset_name ZHS16GBK
ncharset_name AL16UTF16
output_format text
lob_storage infile
clob_byte_order little
trace_level 4
delimiter |

load control file 'config.txt' successful

Read the rest of this entry

,

ODU网页下载到的ODU for Linux (Unix)版本,一般是以“odu_版本号_操作系统_平台.tar.gz”为文件名的文件。上传到你的机器后,使用gunzip和tar解压后,进行少许的配置即可使用。下面举例说明如何在Linux上的使用。

操作系统:Redhat Linux AS 5.0

将odu_210_linux_x86.tar.gz上传到oracle用户的home目录,这里为/export/home/oracle,输入命令:tar xzvf odu_210_linux_x86.tar.gz,即可解开压缩包。解开生成的所有文件和目录在/export/home/oracle/odu目录下。

如果这时候输入命令:./odu,则可能会报下面的错误:

./odu: error while loading shared libraries: libiconv.so.2: cannot open shared object file: No such file or directory

这是由于需要的库文件没有在搜索路径中。执行下面的命令:export LD_LIBRARY_PATH=/export/home/oracle/odu/lib:$LD_LIBRARY_PATH,再运行命令:./odu则会正常运行。

在Solaris平台下,这个环境变量也是LD_LIBRARY_PATH,但在AIX下,这个环境变量则是LIBPATH。

odu压缩包里面所包含的库文件或其不同的版本可能已经存在于系统中。为避免不同版本库文件引起问题,可在设置环境变量时将odu所在的lib目录放到环境变量中的前面部分。

为避免反复设置LD_LIBRARY_PATH和LIBPATH环境变量,可将此环境变量的设置,加入到用户的profile文件中。

UPDATE:
从3.0.7版本开始,各版本的不再需要设置环境变量,因为程序采用静态链接。

--EOF

,

本文将以ODU 2.1.0 for Windows版,介绍如何使用ODU。本文只是一个快速上手指南,以后将详细介绍ODU的使用。

在本网站下载到ODU的Windows版本,是一个ZIP压缩文件,解压缩到一个目录,比如解压到d:\,将会看到d:\odu目录下有如下的文件和目录:

D:\odu>dir
驱动器 D 中的卷没有标签。
卷的序列号是 045E-00DE

D:\odu 的目录

2009-02-08  22:12    <DIR>          .
2009-02-08  22:12    <DIR>          ..
2009-02-08  01:16               218 config.txt
2009-02-08  01:16                99 control.txt
2009-02-08  22:12    <DIR>          data
2004-10-14  00:08           978,432 libiconv2.dll
2009-02-08  19:00           135,168 odu.exe
               4 个文件      1,113,917 字节
               3 个目录  1,738,174,464 可用字节

D:\odu>

如果没有data目录,请手工新建一个。

config.txt文件是ODU的配置文件,默认的配置数据如下:

byte_order little
block_size 8192
data_path data
lob_path lob
charset_name ZHS16GBK
ncharset_name AL16UTF16
output_format text
lob_storage file
clob_byte_order little
trace_level 1
file_header_offset 0
delimiter |

默认的配置基本能满足大部分的需求。

control.txt文件是ODU的数据文件信息文件。你可以理解为类似于Oracle数据库的控制文件,存放了ODU导出数据时需要的Oracle数据文件信息。

control.txt文件中的数据格式为:

表空间号 文件号 相对文件号 文件名 块大小 是否大文件表空间

每列之间用空白分隔,可以只需要前四列,即块大小和是否大文件表空间可省略,块大小省略时,数据文件的默认块大小为config.txt中block_size的大小。下面是一个示例的数据:

#ts #fno   #rfno     filename                                          block_size    bigfile
        0          1          1 D:\ORACLE\ORADATA\XJ\SYSTEM01.DBF              4096    
        1          2          2 D:\ORACLE\ORADATA\XJ\UNDOTBS01.DBF             4096
        3          3          3 D:\ORACLE\ORADATA\XJ\DRSYS01.DBF               4096
        4          4          4 D:\ORACLE\ORADATA\XJ\EXAMPLE01.DBF             4096
        5          5          5 D:\ORACLE\ORADATA\XJ\INDX01.DBF                4096
        6          6          6 D:\ORACLE\ORADATA\XJ\ODM01.DBF                 4096
        7          7          7 D:\ORACLE\ORADATA\XJ\TOOLS03.DBF               4096
        7          8          8 D:\ORACLE\ORADATA\XJ\TOOLS02.DBF               4096
        9          9          9 D:\ORACLE\ORADATA\XJ\XDB01.DBF                 4096
       11         10         10 D:\ORACLE\ORADATA\XJ\TEST01.DBF                2048
       14         11         11 D:\ORACLE\ORADATA\XJ\K16.DBF                  16384

如果数据文件头是完好的,则ODU会自动从文件头里面获取表空间号,文件号,相对文件号,文件块大小等。表空间号,文件号和相对文件号可以写为0。
注意:ODU将检查control.txt文件中的第一个数据文件是否为SYSTEM表空间文件,所以要将SYSTEM表空间的第1个文件放在control.txt文件中的第一行。否则将不能自动获得数据字典数据。

Read the rest of this entry

,