Repository: prometheus/procfs
Pull request #183 (2019-06-19 09:26:34)

Supported for /proc/<pid>/environ
Supported for `/proc/<pid>/environ`

```sh
# cat -e /proc/1/environ
PATH=/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin^@HOSTNAME=33cfa6747903^@TERM=xterm^@GOLANG_VERSION=1.12.5^@GOPATH=/go^@HOME=/root^@
```

- Split `\000` .
- Remove last empty array element.
- About the format of the return value is the slice `[]string` : It uses the same interface as [os.Environ](https://golang.org/pkg/os/#Environ).


Patch:
diff --git a/fixtures.ttar b/fixtures.ttar
index 951d909..2cd62ce 100644
--- a/fixtures.ttar
+++ b/fixtures.ttar
@@ -3,7 +3,7 @@ Directory: fixtures
 Mode: 775
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Directory: fixtures/proc
-Mode: 755
+Mode: 775
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Directory: fixtures/proc/26231
 Mode: 755
@@ -21,6 +21,11 @@ Mode: 644
 Path: fixtures/proc/26231/cwd
 SymlinkTo: /usr/bin
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/proc/26231/environ
+Lines: 1
+PATH=/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binNULLBYTEHOSTNAME=cd24e11f73a5NULLBYTETERM=xtermNULLBYTEGOLANG_VERSION=1.12.5NULLBYTEGOPATH=/goNULLBYTEHOME=/rootNULLBYTEEOF
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Path: fixtures/proc/26231/exe
 SymlinkTo: /usr/bin/vim
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -356,32 +361,62 @@ debug 0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Path: fixtures/proc/mdstat
-Lines: 26
+Lines: 56
 Personalities : [linear] [multipath] [raid0] [raid1] [raid6] [raid5] [raid4] [raid10]
-md3 : active raid6 sda1[8] sdh1[7] sdg1[6] sdf1[5] sde1[11] sdd1[3] sdc1[10] sdb1[9]
+
+md3 : active raid6 sda1[8] sdh1[7] sdg1[6] sdf1[5] sde1[11] sdd1[3] sdc1[10] sdb1[9] sdd1[10](S) sdd2[11](S)
       5853468288 blocks super 1.2 level 6, 64k chunk, algorithm 2 [8/8] [UUUUUUUU]
 
 md127 : active raid1 sdi2[0] sdj2[1]
       312319552 blocks [2/2] [UU]
 
-md0 : active raid1 sdk[2](S) sdi1[0] sdj1[1]
+md0 : active raid1 sdi1[0] sdj1[1]
       248896 blocks [2/2] [UU]
 
-md4 : inactive raid1 sda3[0] sdb3[1]
+md4 : inactive raid1 sda3[0](F) sdb3[1](S)
       4883648 blocks [2/2] [UU]
 
-md6 : active raid1 sdb2[2] sda2[0]
+md6 : active raid1 sdb2[2](F) sdc[1](S) sda2[0]
       195310144 blocks [2/1] [U_]
       [=>...................]  recovery =  8.5% (16775552/195310144) finish=17.0min speed=259783K/sec
 
-md8 : active raid1 sdb1[1] sda1[0]
+md8 : active raid1 sdb1[1] sda1[0] sdc[2](S) sde[3](S)
       195310144 blocks [2/2] [UU]
       [=>...................]  resync =  8.5% (16775552/195310144) finish=17.0min speed=259783K/sec
 
-md7 : active raid6 sdb1[0] sde1[3] sdd1[2] sdc1[1]
+md7 : active raid6 sdb1[0] sde1[3] sdd1[2] sdc1[1](F)
       7813735424 blocks super 1.2 level 6, 512k chunk, algorithm 2 [4/3] [U_UU]
       bitmap: 0/30 pages [0KB], 65536KB chunk
 
+md9 : active raid1 sdc2[2] sdd2[3] sdb2[1] sda2[0] sde[4](F) sdf[5](F) sdg[6](S)
+      523968 blocks super 1.2 [4/4] [UUUU]
+      resync=DELAYED
+
+md10 : active raid0 sda1[0] sdb1[1]
+       314159265 blocks 64k chunks
+
+md11 : active (auto-read-only) raid1 sdb2[0] sdc2[1] sdc3[2](F) hda[4](S) ssdc2[3](S)
+      4190208 blocks super 1.2 [2/2] [UU]
+      resync=PENDING
+
+md12 : active raid0 sdc2[0] sdd2[1]
+      3886394368 blocks super 1.2 512k chunks
+
+md126 : active raid0 sdb[1] sdc[0]
+      1855870976 blocks super external:/md127/0 128k chunks
+
+md219 : inactive sdb[2](S) sdc[1](S) sda[0](S)
+        7932 blocks super external:imsm
+
+md00 : active raid0 xvdb[0]
+      4186624 blocks super 1.2 256k chunks
+
+md120 : active linear sda1[1] sdb1[0]
+        2095104 blocks super 1.2 0k rounding
+
+md101 : active (read-only) raid0 sdb[2] sdd[1] sdc[0]
+      322560 blocks super 1.2 512k chunks
+
 unused devices: <none>
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -1806,3 +1841,287 @@ Lines: 1
 extent_alloc 2 0 0 0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.abcd1abcd2ab
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.abcd1abcd2ab/tpgt_1
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.abcd1abcd2ab/tpgt_1/enable
+Lines: 1
+1
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.abcd1abcd2ab/tpgt_1/lun
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.abcd1abcd2ab/tpgt_1/lun/lun_0
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.abcd1abcd2ab/tpgt_1/lun/lun_0/statistics
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.abcd1abcd2ab/tpgt_1/lun/lun_0/statistics/scsi_tgt_port
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.abcd1abcd2ab/tpgt_1/lun/lun_0/statistics/scsi_tgt_port/in_cmds
+Lines: 1
+104950
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.abcd1abcd2ab/tpgt_1/lun/lun_0/statistics/scsi_tgt_port/read_mbytes
+Lines: 1
+20095
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.abcd1abcd2ab/tpgt_1/lun/lun_0/statistics/scsi_tgt_port/write_mbytes
+Lines: 1
+71235
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/core
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/core/iblock_0
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/core/iblock_0/block_lio_rbd1
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.abcd1abcd2ab/tpgt_1/lun/lun_0/795b7c7026
+SymlinkTo: ../../../../../../target/core/iblock_0/block_lio_rbd1
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/core/iblock_0/block_lio_rbd1/enable
+Lines: 1
+1
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/core/iblock_0/block_lio_rbd1/udev_path
+Lines: 1
+/dev/rbd1
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.8888bbbbddd0
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.8888bbbbddd0/tpgt_1
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.8888bbbbddd0/tpgt_1/enable
+Lines: 1
+1
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.8888bbbbddd0/tpgt_1/lun
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.8888bbbbddd0/tpgt_1/lun/lun_0
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.8888bbbbddd0/tpgt_1/lun/lun_0/statistics
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.8888bbbbddd0/tpgt_1/lun/lun_0/statistics/scsi_tgt_port
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.8888bbbbddd0/tpgt_1/lun/lun_0/statistics/scsi_tgt_port/in_cmds
+Lines: 1
+204950
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.8888bbbbddd0/tpgt_1/lun/lun_0/statistics/scsi_tgt_port/read_mbytes
+Lines: 1
+10325
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.8888bbbbddd0/tpgt_1/lun/lun_0/statistics/scsi_tgt_port/write_mbytes
+Lines: 1
+40325
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/core/rd_mcp_119
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/core/rd_mcp_119/ramdisk_lio_1G
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.8888bbbbddd0/tpgt_1/lun/lun_0/7f4a4eb56d
+SymlinkTo: ../../../../../../target/core/rd_mcp_119/ramdisk_lio_1G
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/core/rd_mcp_119/ramdisk_lio_1G/enable
+Lines: 1
+1
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/core/rd_mcp_119/ramdisk_lio_1G/udev_path
+Lines: 0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:dev.rbd0
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:dev.rbd0/tpgt_1
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:dev.rbd0/tpgt_1/enable
+Lines: 1
+1
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:dev.rbd0/tpgt_1/lun
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:dev.rbd0/tpgt_1/lun/lun_0
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:dev.rbd0/tpgt_1/lun/lun_0/statistics
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:dev.rbd0/tpgt_1/lun/lun_0/statistics/scsi_tgt_port
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:dev.rbd0/tpgt_1/lun/lun_0/statistics/scsi_tgt_port/in_cmds
+Lines: 1
+301950
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:dev.rbd0/tpgt_1/lun/lun_0/statistics/scsi_tgt_port/read_mbytes
+Lines: 1
+10195
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:dev.rbd0/tpgt_1/lun/lun_0/statistics/scsi_tgt_port/write_mbytes
+Lines: 1
+30195
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/core/fileio_0
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/core/fileio_1
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/core/fileio_1/file_lio_1G
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:dev.rbd0/tpgt_1/lun/lun_0/fff5e16686
+SymlinkTo: ../../../../../../target/core/fileio_1/file_lio_1G
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/core/fileio_1/file_lio_1G/enable
+Lines: 1
+1
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/core/fileio_1/file_lio_1G/udev_path
+Lines: 1
+/home/iscsi/file_back_1G
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:sn.ramdemo
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:sn.ramdemo/tpgt_1
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:sn.ramdemo/tpgt_1/enable
+Lines: 1
+1
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:sn.ramdemo/tpgt_1/lun
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:sn.ramdemo/tpgt_1/lun/lun_0
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:sn.ramdemo/tpgt_1/lun/lun_0/statistics
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:sn.ramdemo/tpgt_1/lun/lun_0/statistics/scsi_tgt_port
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:sn.ramdemo/tpgt_1/lun/lun_0/statistics/scsi_tgt_port/in_cmds
+Lines: 1
+1234
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:sn.ramdemo/tpgt_1/lun/lun_0/statistics/scsi_tgt_port/read_mbytes
+Lines: 1
+1504
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:sn.ramdemo/tpgt_1/lun/lun_0/statistics/scsi_tgt_port/write_mbytes
+Lines: 1
+4733
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/core/rbd_0
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/kernel/config/target/core/rbd_0/iscsi-images-demo
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:sn.ramdemo/tpgt_1/lun/lun_0/eba1edf893
+SymlinkTo: ../../../../../../target/core/rbd_0/iscsi-images-demo
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/core/rbd_0/iscsi-images-demo/enable
+Lines: 1
+1
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/kernel/config/target/core/rbd_0/iscsi-images-demo/udev_path
+Lines: 1
+/dev/rbd/iscsi-images/demo
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/rbd
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/rbd/0
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/rbd/0/pool
+Lines: 1
+iscsi-images
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/rbd/0/name
+Lines: 1
+demo
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/rbd/1
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/rbd/1/pool
+Lines: 1
+wrong-images
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/rbd/1/name
+Lines: 1
+wrong
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/internal/fs/fs.go b/internal/fs/fs.go
index c66a1cf..7ddfd6b 100644
--- a/internal/fs/fs.go
+++ b/internal/fs/fs.go
@@ -25,6 +25,9 @@ const (
 
 	// DefaultSysMountPoint is the common mount point of the sys filesystem.
 	DefaultSysMountPoint = "/sys"
+
+	// DefaultConfigfsMountPoint is the commont mount point of the configfs
+	DefaultConfigfsMountPoint = "/sys/kernel/config"
 )
 
 // FS represents a pseudo-filesystem, normally /proc or /sys, which provides an
diff --git a/iscsi/get.go b/iscsi/get.go
new file mode 100644
index 0000000..489a655
--- /dev/null
+++ b/iscsi/get.go
@@ -0,0 +1,253 @@
+// Copyright 2019 The Prometheus Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package iscsi
+
+import (
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strconv"
+	"strings"
+
+	"github.com/prometheus/procfs/internal/util"
+)
+
+// GetStats is the main iscsi status information func
+// building the path and prepare info for enable iscsi
+func GetStats(iqnPath string) (*Stats, error) {
+	var istats Stats
+
+	istats.Name = filepath.Base(iqnPath)
+	istats.RootPath = filepath.Dir(iqnPath)
+
+	matches, err := filepath.Glob(filepath.Join(iqnPath, "tpgt*"))
+	if err != nil {
+		return nil, fmt.Errorf("iscsi: GetStats: get TPGT path error %v", err)
+	}
+	istats.Tpgt = make([]TPGT, len(matches))
+
+	for i, tpgtPath := range matches {
+		istats.Tpgt[i].Name = filepath.Base(tpgtPath)
+		istats.Tpgt[i].TpgtPath = tpgtPath
+		istats.Tpgt[i].IsEnable, _ = isPathEnable(tpgtPath)
+		if istats.Tpgt[i].IsEnable {
+			matchesLunsPath, _ := filepath.Glob(filepath.Join(tpgtPath, "lun/lun*"))
+
+			for _, lunPath := range matchesLunsPath {
+				lun, err := getLunLinkTarget(lunPath)
+				if err == nil {
+					istats.Tpgt[i].Luns = append(istats.Tpgt[i].Luns, lun)
+				}
+			}
+		}
+	}
+	return &istats, nil
+}
+
+// isPathEnable is a utility function
+// check if the file "enable" contain enable message
+func isPathEnable(path string) (bool, error) {
+	enableReadout, err := ioutil.ReadFile(filepath.Join(path, "enable"))
+	if err != nil {
+		return false, fmt.Errorf("iscsi: isPathEnable ReadFile error %v", err)
+	}
+	isEnable, err := strconv.ParseBool(strings.TrimSpace(string(enableReadout)))
+	if err != nil {
+		return false, fmt.Errorf("iscsi: isPathEnable ParseBool error %v", err)
+	}
+	return isEnable, nil
+}
+
+func getLunLinkTarget(lunPath string) (lunObject LUN, err error) {
+	lunObject.Name = filepath.Base(lunPath)
+	lunObject.LunPath = lunPath
+	files, err := ioutil.ReadDir(lunPath)
+	if err != nil {
+		return lunObject, fmt.Errorf("getLunLinkTarget: ReadDir path %s error %v", lunPath, err)
+	}
+	for _, file := range files {
+		fileInfo, _ := os.Lstat(filepath.Join(lunPath, file.Name()))
+		if fileInfo.Mode()&os.ModeSymlink != 0 {
+			target, err := os.Readlink(filepath.Join(lunPath, fileInfo.Name()))
+			if err != nil {
+				return lunObject, fmt.Errorf("getLunLinkTarget: Readlink err %v", err)
+			}
+			targetPath, objectName := filepath.Split(target)
+			_, typeWithNumber := filepath.Split(filepath.Clean(targetPath))
+
+			underscore := strings.LastIndex(typeWithNumber, "_")
+
+			if underscore != -1 {
+				lunObject.Backstore = typeWithNumber[:underscore]
+				lunObject.TypeNumber = typeWithNumber[underscore+1:]
+			}
+
+			lunObject.ObjectName = objectName
+			return lunObject, nil
+		}
+	}
+	return lunObject, errors.New("iscsi: getLunLinkTarget: Lun Link does not exist")
+}
+
+// ReadWriteOPS read and return the stat of read and write in megabytes,
+// and total commands that send to the target
+func ReadWriteOPS(iqnPath string, tpgt string, lun string) (readmb uint64,
+	writemb uint64, iops uint64, err error) {
+
+	readmbPath := filepath.Join(iqnPath, tpgt, "lun", lun,
+		"statistics/scsi_tgt_port/read_mbytes")
+	readmb, err = util.ReadUintFromFile(readmbPath)
+	if err != nil {
+		return 0, 0, 0, fmt.Errorf("iscsi: ReadWriteOPS: read_mbytes error file %s and %v",
+			readmbPath, err)
+	}
+
+	writembPath := filepath.Join(iqnPath, tpgt, "lun", lun,
+		"statistics/scsi_tgt_port/write_mbytes")
+	writemb, err = util.ReadUintFromFile(writembPath)
+	if err != nil {
+		return 0, 0, 0, fmt.Errorf("iscsi: ReadWriteOPS: write_mbytes error file %s and %v",
+			writembPath, err)
+	}
+
+	iopsPath := filepath.Join(iqnPath, tpgt, "lun", lun,
+		"statistics/scsi_tgt_port/in_cmds")
+	iops, err = util.ReadUintFromFile(iopsPath)
+	if err != nil {
+		return 0, 0, 0, fmt.Errorf("iscsi: ReadWriteOPS: in_cmds error file %s and %v",
+			iopsPath, err)
+	}
+
+	return readmb, writemb, iops, nil
+}
+
+// GetFileioUdev is getting the actual info to build up
+// the FILEIO data and match with the enable target
+func (fileio FILEIO) GetFileioUdev(targetCorePath string, fileioNumber string,
+	objectName string) (fio *FILEIO, err error) {
+
+	fileio.Name = "fileio_" + fileioNumber
+	fileio.Fnumber = fileioNumber
+	fileio.ObjectName = objectName
+
+	udevPath := filepath.Join(targetCorePath, fileio.Name, fileio.ObjectName, "udev_path")
+
+	if _, err := os.Stat(udevPath); os.IsNotExist(err) {
+		return nil, fmt.Errorf("iscsi: GetFileioUdev: fileio_%s is missing file name", fileio.Fnumber)
+	}
+	filename, err := ioutil.ReadFile(udevPath)
+	if err != nil {
+		return nil, fmt.Errorf("iscsi: GetFileioUdev: Cannot read filename from udev link :%s", udevPath)
+	}
+	fileio.Filename = strings.TrimSpace(string(filename))
+
+	return &fileio, nil
+}
+
+// GetIblockUdev is getting the actual info to build up
+// the IBLOCK data and match with the enable target
+func (iblock IBLOCK) GetIblockUdev(targetCorePath string, iblockNumber string,
+	objectName string) (ib *IBLOCK, err error) {
+
+	iblock.Name = "iblock_" + iblockNumber
+	iblock.Bnumber = iblockNumber
+	iblock.ObjectName = objectName
+
+	udevPath := filepath.Join(targetCorePath, iblock.Name, iblock.ObjectName, "udev_path")
+
+	if _, err := os.Stat(udevPath); os.IsNotExist(err) {
+		return nil, fmt.Errorf("iscsi: GetIBlockUdev: iblock_%s is missing file name", iblock.Bnumber)
+	}
+	filename, err := ioutil.ReadFile(udevPath)
+	if err != nil {
+		return nil, fmt.Errorf("iscsi: GetIBlockUdev: Cannot read iblock from udev link :%s", udevPath)
+	}
+	iblock.Iblock = strings.TrimSpace(string(filename))
+
+	return &iblock, nil
+}
+
+// GetRBDMatch is getting the actual info to build up
+// the RBD data and match with the enable target
+func (rbd RBD) GetRBDMatch(sysDevicePath string, rbdNumber string, poolImage string) (r *RBD, err error) {
+
+	rbd.Name = "rbd_" + rbdNumber
+	rbd.Rnumber = rbdNumber
+
+	systemRbds, err := filepath.Glob(filepath.Join(sysDevicePath, "/devices/rbd/[0-9]*"))
+	if err != nil {
+		return nil, fmt.Errorf("iscsi: GetRBDMatch: Cannot find any rbd block")
+	}
+
+	for systemRbdNumber, systemRbdPath := range systemRbds {
+		var systemPool, systemImage string = "", ""
+		systemPoolPath := filepath.Join(systemRbdPath, "pool")
+		if _, err := os.Stat(systemPoolPath); os.IsNotExist(err) {
+			continue
+		}
+		bSystemPool, err := ioutil.ReadFile(systemPoolPath)
+		if err != nil {
+			continue
+		} else {
+			systemPool = strings.TrimSpace(string(bSystemPool))
+		}
+
+		systemImagePath := filepath.Join(systemRbdPath, "name")
+		if _, err := os.Stat(systemImagePath); os.IsNotExist(err) {
+			continue
+		}
+		bSystemImage, err := ioutil.ReadFile(systemImagePath)
+		if err != nil {
+			continue
+		} else {
+			systemImage = strings.TrimSpace(string(bSystemImage))
+		}
+
+		if strings.Compare(strconv.FormatInt(int64(systemRbdNumber), 10), rbdNumber) == 0 &&
+			matchPoolImage(systemPool, systemImage, poolImage) {
+			rbd.Pool = systemPool
+			rbd.Image = systemImage
+			return &rbd, nil
+		}
+	}
+	return nil, nil
+}
+
+// GetRDMCPPath is getting the actual info to build up RDMCP data
+func (rdmcp RDMCP) GetRDMCPPath(targetCorePath string, rdmcpNumber string, objectName string) (r *RDMCP, err error) {
+	rdmcp.Name = "rd_mcp_" + rdmcpNumber
+	rdmcp.ObjectName = objectName
+
+	rdmcpPath := filepath.Join(targetCorePath, rdmcp.Name, rdmcp.ObjectName)
+
+	if _, err := os.Stat(rdmcpPath); os.IsNotExist(err) {
+		return nil, fmt.Errorf("iscsi: GetRDMCPPath: %s does not exist", rdmcpPath)
+	}
+	isEnable, err := isPathEnable(rdmcpPath)
+	if err != nil {
+		return nil, fmt.Errorf("iscsi: GetRDMCPPath: error %v", err)
+	}
+	if isEnable {
+		return &rdmcp, nil
+	}
+	return nil, nil
+}
+
+func matchPoolImage(pool string, image string, matchPoolImage string) (isEqual bool) {
+	var poolImage = fmt.Sprintf("%s-%s", pool, image)
+	return strings.Compare(poolImage, matchPoolImage) == 0
+}
diff --git a/iscsi/iscsi.go b/iscsi/iscsi.go
new file mode 100644
index 0000000..a155049
--- /dev/null
+++ b/iscsi/iscsi.go
@@ -0,0 +1,126 @@
+// Copyright 2019 The Prometheus Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package iscsi
+
+import (
+	"path/filepath"
+	"strings"
+
+	"github.com/prometheus/procfs/internal/fs"
+)
+
+// iscsi target started with /sys/kernel/config/target/iscsi/iqn*
+// configfs + target/iscsi/iqn*
+// IqnGlob is representing all the possible IQN
+const IqnGlob = "target/iscsi/iqn*"
+
+// TargetCore static path /sys/kernel/config/target/core for node_exporter
+// reading runtime status
+const TargetCore = "target/core"
+
+// FS represents the pseudo-filesystem configfs, which provides an interface to
+// iscsi kernel data structures in /sys/kernel/config.
+type FS struct {
+	configfs *fs.FS
+}
+
+// NewFS returns a new configfs mounted under the given mount point. It will
+// error and return empty FS if the mount point can't be read. For the ease of
+// use, an empty string parameter configfsMountPoint will call internal fs for
+// the default sys path as /sys/kernel/config
+func NewFS(configfsMountPoint string) (FS, error) {
+	if strings.TrimSpace(configfsMountPoint) == "" {
+		configfsMountPoint = fs.DefaultConfigfsMountPoint
+	}
+	configfs, err := fs.NewFS(configfsMountPoint)
+	if err != nil {
+		return FS{}, err
+	}
+	return FS{&configfs}, nil
+}
+
+// ISCSIStats getting iscsi runtime information
+func (fs FS) ISCSIStats() ([]*Stats, error) {
+	matches, err := filepath.Glob(fs.configfs.Path(IqnGlob))
+	if err != nil {
+		return nil, err
+	}
+
+	stats := make([]*Stats, 0, len(matches))
+	for _, iqnPath := range matches {
+		// stats
+		s, err := GetStats(iqnPath)
+		if err != nil {
+			return nil, err
+		}
+		stats = append(stats, s)
+	}
+
+	return stats, nil
+}
+
+// TPGT struct for sys target portal group tag info
+type TPGT struct {
+	Name     string // name of the tpgt group
+	TpgtPath string // file path of tpgt
+	IsEnable bool   // is the tpgt enable
+	Luns     []LUN  // the Luns that tpgt has
+}
+
+// LUN struct for sys logical unit number info
+type LUN struct {
+	Name       string // name of the lun
+	LunPath    string // file path of the lun
+	Backstore  string // backstore of the lun
+	ObjectName string // place holder for object
+	TypeNumber string // place holder for number of the device
+}
+
+// FILEIO struct for backstore info
+type FILEIO struct {
+	Name       string // name of the fileio
+	Fnumber    string // number related to the backstore
+	ObjectName string // place holder for object in iscsi object
+	Filename   string // link to the actual file being export
+}
+
+// IBLOCK struct for backstore info
+type IBLOCK struct {
+	Name       string // name of the iblock
+	Bnumber    string // number related to the backstore
+	ObjectName string // place holder for object in iscsi object
+	Iblock     string // link to the actual block being export
+}
+
+// RBD struct for backstore info
+type RBD struct {
+	Name    string // name of the rbd
+	Rnumber string // number related to the backstore
+	Pool    string // place holder for the rbd pool
+	Image   string // place holder for the rbd image
+}
+
+// RDMCP struct for backstore info
+type RDMCP struct {
+	Name       string // name of the rdm_cp
+	ObjectName string // place holder for object name
+}
+
+// Stats struct for all targets info
+type Stats struct {
+	Name     string
+	Tpgt     []TPGT
+	RootPath string
+}
diff --git a/mdstat.go b/mdstat.go
index 71c1067..2af3ada 100644
--- a/mdstat.go
+++ b/mdstat.go
@@ -22,8 +22,8 @@ import (
 )
 
 var (
-	statuslineRE = regexp.MustCompile(`(\d+) blocks .*\[(\d+)/(\d+)\] \[[U_]+\]`)
-	buildlineRE  = regexp.MustCompile(`\((\d+)/\d+\)`)
+	statusLineRE   = regexp.MustCompile(`(\d+) blocks .*\[(\d+)/(\d+)\] \[[U_]+\]`)
+	recoveryLineRE = regexp.MustCompile(`\((\d+)/\d+\)`)
 )
 
 // MDStat holds info parsed from /proc/mdstat.
@@ -34,8 +34,12 @@ type MDStat struct {
 	ActivityState string
 	// Number of active disks.
 	DisksActive int64
-	// Total number of disks the device consists of.
+	// Total number of disks the device requires.
 	DisksTotal int64
+	// Number of failed disks.
+	DisksFailed int64
+	// Spare disks in the device.
+	DisksSpare int64
 	// Number of blocks the device holds.
 	BlocksTotal int64
 	// Number of blocks on the device that are in sync.
@@ -59,29 +63,38 @@ func (fs FS) MDStat() ([]MDStat, error) {
 
 // parseMDStat parses data from mdstat file (/proc/mdstat) and returns a slice of
 // structs containing the relevant info.
-func parseMDStat(mdstatData []byte) ([]MDStat, error) {
+func parseMDStat(mdStatData []byte) ([]MDStat, error) {
 	mdStats := []MDStat{}
-	lines := strings.Split(string(mdstatData), "\n")
-	for i, l := range lines {
-		if strings.TrimSpace(l) == "" || l[0] == ' ' ||
-			strings.HasPrefix(l, "Personalities") || strings.HasPrefix(l, "unused") {
+	lines := strings.Split(string(mdStatData), "\n")
+
+	for i, line := range lines {
+		if strings.TrimSpace(line) == "" || line[0] == ' ' ||
+			strings.HasPrefix(line, "Personalities") ||
+			strings.HasPrefix(line, "unused") {
 			continue
 		}
 
-		deviceFields := strings.Fields(l)
+		deviceFields := strings.Fields(line)
 		if len(deviceFields) < 3 {
-			return nil, fmt.Errorf("not enough fields in mdline (expected at least 3): %s", l)
+			return nil, fmt.Errorf("not enough fields in mdline (expected at least 3): %s", line)
 		}
-		mdName := deviceFields[0]
-		activityState := deviceFields[2]
+		mdName := deviceFields[0] // mdx
+		state := deviceFields[2]  // active or inactive
 
 		if len(lines) <= i+3 {
-			return mdStats, fmt.Errorf("missing lines for md device %s", mdName)
+			return nil, fmt.Errorf(
+				"error parsing %s: too few lines for md device",
+				mdName,
+			)
 		}
 
-		active, total, size, err := evalStatusLine(lines[i+1])
+		// Failed disks have the suffix (F) & Spare disks have the suffix (S).
+		fail := int64(strings.Count(line, "(F)"))
+		spare := int64(strings.Count(line, "(S)"))
+		active, total, size, err := evalStatusLine(lines[i], lines[i+1])
+
 		if err != nil {
-			return nil, err
+			return nil, fmt.Errorf("error parsing md device lines: %s", err)
 		}
 
 		syncLineIdx := i + 2
@@ -89,20 +102,38 @@ func parseMDStat(mdstatData []byte) ([]MDStat, error) {
 			syncLineIdx++
 		}
 
-		// If device is recovering/syncing at the moment, get the number of currently
+		// If device is syncing at the moment, get the number of currently
 		// synced bytes, otherwise that number equals the size of the device.
 		syncedBlocks := size
-		if strings.Contains(lines[syncLineIdx], "recovery") || strings.Contains(lines[syncLineIdx], "resync") {
-			syncedBlocks, err = evalRecoveryLine(lines[syncLineIdx])
-			if err != nil {
-				return nil, err
+		recovering := strings.Contains(lines[syncLineIdx], "recovery")
+		resyncing := strings.Contains(lines[syncLineIdx], "resync")
+
+		// Append recovery and resyncing state info.
+		if recovering || resyncing {
+			if recovering {
+				state = "recovering"
+			} else {
+				state = "resyncing"
+			}
+
+			// Handle case when resync=PENDING or resync=DELAYED.
+			if strings.Contains(lines[syncLineIdx], "PENDING") ||
+				strings.Contains(lines[syncLineIdx], "DELAYED") {
+				syncedBlocks = 0
+			} else {
+				syncedBlocks, err = evalRecoveryLine(lines[syncLineIdx])
+				if err != nil {
+					return nil, fmt.Errorf("error parsing sync line in md device %s: %s", mdName, err)
+				}
 			}
 		}
 
 		mdStats = append(mdStats, MDStat{
 			Name:          mdName,
-			ActivityState: activityState,
+			ActivityState: state,
 			DisksActive:   active,
+			DisksFailed:   fail,
+			DisksSpare:    spare,
 			DisksTotal:    total,
 			BlocksTotal:   size,
 			BlocksSynced:  syncedBlocks,
@@ -112,39 +143,51 @@ func parseMDStat(mdstatData []byte) ([]MDStat, error) {
 	return mdStats, nil
 }
 
-func evalStatusLine(statusline string) (active, total, size int64, err error) {
-	matches := statuslineRE.FindStringSubmatch(statusline)
-	if len(matches) != 4 {
-		return 0, 0, 0, fmt.Errorf("unexpected statusline: %s", statusline)
-	}
+func evalStatusLine(deviceLine, statusLine string) (active, total, size int64, err error) {
 
-	size, err = strconv.ParseInt(matches[1], 10, 64)
+	sizeStr := strings.Fields(statusLine)[0]
+	size, err = strconv.ParseInt(sizeStr, 10, 64)
 	if err != nil {
-		return 0, 0, 0, fmt.Errorf("unexpected statusline %s: %s", statusline, err)
+		return 0, 0, 0, fmt.Errorf("unexpected statusLine %s: %s", statusLine, err)
+	}
+
+	if strings.Contains(deviceLine, "raid0") || strings.Contains(deviceLine, "linear") {
+		// In the device deviceLine, only disks have a number associated with them in [].
+		total = int64(strings.Count(deviceLine, "["))
+		return total, total, size, nil
+	}
+
+	if strings.Contains(deviceLine, "inactive") {
+		return 0, 0, size, nil
+	}
+
+	matches := statusLineRE.FindStringSubmatch(statusLine)
+	if len(matches) != 4 {
+		return 0, 0, 0, fmt.Errorf("couldn't find all the substring matches: %s", statusLine)
 	}
 
 	total, err = strconv.ParseInt(matches[2], 10, 64)
 	if err != nil {
-		return 0, 0, 0, fmt.Errorf("unexpected statusline %s: %s", statusline, err)
+		return 0, 0, 0, fmt.Errorf("unexpected statusLine %s: %s", statusLine, err)
 	}
 
 	active, err = strconv.ParseInt(matches[3], 10, 64)
 	if err != nil {
-		return 0, 0, 0, fmt.Errorf("unexpected statusline %s: %s", statusline, err)
+		return 0, 0, 0, fmt.Errorf("unexpected statusLine %s: %s", statusLine, err)
 	}
 
 	return active, total, size, nil
 }
 
-func evalRecoveryLine(buildline string) (syncedBlocks int64, err error) {
-	matches := buildlineRE.FindStringSubmatch(buildline)
+func evalRecoveryLine(recoveryLine string) (syncedBlocks int64, err error) {
+	matches := recoveryLineRE.FindStringSubmatch(recoveryLine)
 	if len(matches) != 2 {
-		return 0, fmt.Errorf("unexpected buildline: %s", buildline)
+		return 0, fmt.Errorf("unexpected recoveryLine: %s", recoveryLine)
 	}
 
 	syncedBlocks, err = strconv.ParseInt(matches[1], 10, 64)
 	if err != nil {
-		return 0, fmt.Errorf("%s in buildline: %s", err, buildline)
+		return 0, fmt.Errorf("%s in recoveryLine: %s", err, recoveryLine)
 	}
 
 	return syncedBlocks, nil
diff --git a/mountinfo.go b/mountinfo.go
new file mode 100644
index 0000000..61fa618
--- /dev/null
+++ b/mountinfo.go
@@ -0,0 +1,178 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package procfs
+
+import (
+	"bufio"
+	"fmt"
+	"io"
+	"os"
+	"strconv"
+	"strings"
+)
+
+var validOptionalFields = map[string]bool{
+	"shared":         true,
+	"master":         true,
+	"propagate_from": true,
+	"unbindable":     true,
+}
+
+// A MountInfo is a type that describes the details, options
+// for each mount, parsed from /proc/self/mountinfo.
+// The fields described in each entry of /proc/self/mountinfo
+// is described in the following man page.
+// http://man7.org/linux/man-pages/man5/proc.5.html
+type MountInfo struct {
+	// Unique Id for the mount
+	MountId int
+	// The Id of the parent mount
+	ParentId int
+	// The value of `st_dev` for the files on this FS
+	MajorMinorVer string
+	// The pathname of the directory in the FS that forms
+	// the root for this mount
+	Root string
+	// The pathname of the mount point relative to the root
+	MountPoint string
+	// Mount options
+	Options map[string]string
+	// Zero or more optional fields
+	OptionalFields map[string]string
+	// The Filesystem type
+	FSType string
+	// FS specific information or "none"
+	Source string
+	// Superblock options
+	SuperOptions map[string]string
+}
+
+// Returns part of the mountinfo line, if it exists, else an empty string.
+func getStringSliceElement(parts []string, idx int, defaultValue string) string {
+	if idx >= len(parts) {
+		return defaultValue
+	}
+	return parts[idx]
+}
+
+// Reads each line of the mountinfo file, and returns a list of formatted MountInfo structs.
+func parseMountInfo(r io.Reader) ([]*MountInfo, error) {
+	mounts := []*MountInfo{}
+	scanner := bufio.NewScanner(r)
+	for scanner.Scan() {
+		mountString := scanner.Text()
+		parsedMounts, err := parseMountInfoString(mountString)
+		if err != nil {
+			return nil, err
+		}
+		mounts = append(mounts, parsedMounts)
+	}
+
+	err := scanner.Err()
+	return mounts, err
+}
+
+// Parses a mountinfo file line, and converts it to a MountInfo struct.
+// An important check here is to see if the hyphen separator, as if it does not exist,
+// it means that the line is malformed.
+func parseMountInfoString(mountString string) (*MountInfo, error) {
+	var err error
+
+	// OptionalFields can be zero, hence these checks to ensure we do not populate the wrong values in the wrong spots
+	separatorIndex := strings.Index(mountString, "-")
+	if separatorIndex == -1 {
+		return nil, fmt.Errorf("no separator found in mountinfo string: %s", mountString)
+	}
+	beforeFields := strings.Fields(mountString[:separatorIndex])
+	afterFields := strings.Fields(mountString[separatorIndex+1:])
+	if (len(beforeFields) + len(afterFields)) < 7 {
+		return nil, fmt.Errorf("too few fields")
+	}
+
+	mount := &MountInfo{
+		MajorMinorVer:  getStringSliceElement(beforeFields, 2, ""),
+		Root:           getStringSliceElement(beforeFields, 3, ""),
+		MountPoint:     getStringSliceElement(beforeFields, 4, ""),
+		Options:        mountOptionsParser(getStringSliceElement(beforeFields, 5, "")),
+		OptionalFields: nil,
+		FSType:         getStringSliceElement(afterFields, 0, ""),
+		Source:         getStringSliceElement(afterFields, 1, ""),
+		SuperOptions:   mountOptionsParser(getStringSliceElement(afterFields, 2, "")),
+	}
+
+	mount.MountId, err = strconv.Atoi(getStringSliceElement(beforeFields, 0, ""))
+	if err != nil {
+		return nil, fmt.Errorf("failed to parse mount ID")
+	}
+	mount.ParentId, err = strconv.Atoi(getStringSliceElement(beforeFields, 1, ""))
+	if err != nil {
+		return nil, fmt.Errorf("failed to parse parent ID")
+	}
+	// Has optional fields, which is a space separated list of values.
+	// Example: shared:2 master:7
+	if len(beforeFields) > 6 {
+		mount.OptionalFields = make(map[string]string)
+		optionalFields := beforeFields[6:]
+		for _, field := range optionalFields {
+			optionSplit := strings.Split(field, ":")
+			target, value := optionSplit[0], ""
+			if len(optionSplit) == 2 {
+				value = optionSplit[1]
+			}
+			// Checks if the 'keys' in the optional fields in the mountinfo line are acceptable.
+			// Allowed 'keys' are shared, master, propagate_from, unbindable.
+			if _, ok := validOptionalFields[target]; ok {
+				mount.OptionalFields[target] = value
+			}
+		}
+	}
+	return mount, nil
+}
+
+// Parses the mount options, superblock options.
+func mountOptionsParser(mountOptions string) map[string]string {
+	opts := make(map[string]string)
+	options := strings.Split(mountOptions, ",")
+	for _, opt := range options {
+		splitOption := strings.Split(opt, "=")
+		if len(splitOption) < 2 {
+			key := splitOption[0]
+			opts[key] = ""
+		} else {
+			key, value := splitOption[0], splitOption[1]
+			opts[key] = value
+		}
+	}
+	return opts
+}
+
+// Retrieves mountinfo information from `/proc/self/mountinfo`.
+func GetMounts() ([]*MountInfo, error) {
+	f, err := os.Open("/proc/self/mountinfo")
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	return parseMountInfo(f)
+}
+
+// Retrieves mountinfo information from a processes' `/proc/<pid>/mountinfo`.
+func GetProcMounts(pid int) ([]*MountInfo, error) {
+	f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid))
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	return parseMountInfo(f)
+}
diff --git a/proc.go b/proc.go
index 8a84301..41c148d 100644
--- a/proc.go
+++ b/proc.go
@@ -247,6 +247,20 @@ func (p Proc) MountStats() ([]*Mount, error) {
 	return parseMountStats(f)
 }
 
+// MountInfo retrieves mount information for mount points in a
+// process's namespace.
+// It supplies information missing in `/proc/self/mounts` and
+// fixes various other problems with that file too.
+func (p Proc) MountInfo() ([]*MountInfo, error) {
+	f, err := os.Open(p.path("mountinfo"))
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+
+	return parseMountInfo(f)
+}
+
 func (p Proc) fileDescriptors() ([]string, error) {
 	d, err := os.Open(p.path("fd"))
 	if err != nil {
diff --git a/proc_environ.go b/proc_environ.go
new file mode 100644
index 0000000..7172bb5
--- /dev/null
+++ b/proc_environ.go
@@ -0,0 +1,43 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package procfs
+
+import (
+	"io/ioutil"
+	"os"
+	"strings"
+)
+
+// Environ reads process environments from /proc/<pid>/environ
+func (p Proc) Environ() ([]string, error) {
+	environments := make([]string, 0)
+
+	f, err := os.Open(p.path("environ"))
+	if err != nil {
+		return environments, err
+	}
+	defer f.Close()
+
+	data, err := ioutil.ReadAll(f)
+	if err != nil {
+		return environments, err
+	}
+
+	environments = strings.Split(string(data), "\000")
+	if len(environments) > 0 {
+		environments = environments[:len(environments)-1]
+	}
+
+	return environments, nil
+}


Test patch:
diff --git a/fs_test.go b/fs_test.go
index 6e57745..1764bd4 100644
--- a/fs_test.go
+++ b/fs_test.go
@@ -33,7 +33,7 @@ func TestNewFS(t *testing.T) {
 func getProcFixtures(t *testing.T) FS {
 	fs, err := NewFS(procTestFixtures)
 	if err != nil {
-		t.Fatal(err)
+		t.Fatalf("Creating pseudo fs from getProcFixtures failed at fixtures/proc with error: %s", err)
 	}
 	return fs
 }
diff --git a/iscsi/get_test.go b/iscsi/get_test.go
new file mode 100644
index 0000000..fb69990
--- /dev/null
+++ b/iscsi/get_test.go
@@ -0,0 +1,212 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package iscsi_test
+
+import (
+	"path/filepath"
+	"reflect"
+	"testing"
+
+	"github.com/prometheus/procfs/iscsi"
+)
+
+func TestGetStats(t *testing.T) {
+	tests := []struct {
+		invalid bool
+		stat    *iscsi.Stats
+	}{
+		{
+			stat: &iscsi.Stats{
+				Name: "iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.8888bbbbddd0",
+				Tpgt: []iscsi.TPGT{
+					{
+						Name:     "tpgt_1",
+						TpgtPath: "../fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.8888bbbbddd0/tpgt_1",
+						IsEnable: true,
+						Luns: []iscsi.LUN{
+							{
+								Name:       "lun_0",
+								LunPath:    "../fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.8888bbbbddd0/tpgt_1/lun/lun_0",
+								Backstore:  "rd_mcp",
+								ObjectName: "ramdisk_lio_1G",
+								TypeNumber: "119",
+							},
+						},
+					},
+				},
+				RootPath: "../fixtures/sys/kernel/config/target/iscsi",
+			},
+		},
+		{
+			stat: &iscsi.Stats{
+				Name: "iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.abcd1abcd2ab",
+				Tpgt: []iscsi.TPGT{
+					{
+						Name:     "tpgt_1",
+						TpgtPath: "../fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.abcd1abcd2ab/tpgt_1",
+						IsEnable: true,
+						Luns: []iscsi.LUN{
+							{
+								Name:       "lun_0",
+								LunPath:    "../fixtures/sys/kernel/config/target/iscsi/iqn.2003-01.org.linux-iscsi.osd1.x8664:sn.abcd1abcd2ab/tpgt_1/lun/lun_0",
+								Backstore:  "iblock",
+								ObjectName: "block_lio_rbd1",
+								TypeNumber: "0",
+							},
+						},
+					},
+				},
+				RootPath: "../fixtures/sys/kernel/config/target/iscsi",
+			},
+		},
+		{
+			stat: &iscsi.Stats{
+				Name: "iqn.2016-11.org.linux-iscsi.igw.x86:dev.rbd0",
+				Tpgt: []iscsi.TPGT{
+					{
+						Name:     "tpgt_1",
+						TpgtPath: "../fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:dev.rbd0/tpgt_1",
+						IsEnable: true,
+						Luns: []iscsi.LUN{
+							{
+								Name:       "lun_0",
+								LunPath:    "../fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:dev.rbd0/tpgt_1/lun/lun_0",
+								Backstore:  "fileio",
+								ObjectName: "file_lio_1G",
+								TypeNumber: "1",
+							},
+						},
+					},
+				},
+				RootPath: "../fixtures/sys/kernel/config/target/iscsi",
+			},
+		},
+		{
+			stat: &iscsi.Stats{
+				Name: "iqn.2016-11.org.linux-iscsi.igw.x86:sn.ramdemo",
+				Tpgt: []iscsi.TPGT{
+					{
+						Name:     "tpgt_1",
+						TpgtPath: "../fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:sn.ramdemo/tpgt_1",
+						IsEnable: true,
+						Luns: []iscsi.LUN{
+							{
+								Name:       "lun_0",
+								LunPath:    "../fixtures/sys/kernel/config/target/iscsi/iqn.2016-11.org.linux-iscsi.igw.x86:sn.ramdemo/tpgt_1/lun/lun_0",
+								Backstore:  "rbd",
+								ObjectName: "iscsi-images-demo",
+								TypeNumber: "0",
+							},
+						},
+					},
+				},
+				RootPath: "../fixtures/sys/kernel/config/target/iscsi",
+			},
+		},
+	}
+
+	readTests := []struct {
+		read  uint64
+		write uint64
+		iops  uint64
+	}{
+		{10325, 40325, 204950},
+		{20095, 71235, 104950},
+		{10195, 30195, 301950},
+		{1504, 4733, 1234},
+	}
+
+	sysfs, err := iscsi.NewFS("../fixtures/sys/kernel/config")
+	if err != nil {
+		t.Fatalf("failed to access xfs fs: %v", err)
+	}
+	sysfsStat, err := sysfs.ISCSIStats()
+	statSize := len(sysfsStat)
+	if statSize != 4 {
+		t.Errorf("fixtures size does not match %d", statSize)
+	}
+	if err != nil {
+		t.Errorf("unexpected test fixtures")
+	}
+
+	for i, stat := range sysfsStat {
+		want, have := tests[i].stat, stat
+		if !reflect.DeepEqual(want, have) {
+			t.Errorf("unexpected iSCSI stats:\nwant:\n%v\nhave:\n%v", want, have)
+		} else {
+			readMB, writeMB, iops, err := iscsi.ReadWriteOPS(stat.RootPath+"/"+stat.Name,
+				stat.Tpgt[0].Name, stat.Tpgt[0].Luns[0].Name)
+			if err != nil {
+				t.Errorf("unexpected iSCSI ReadWriteOPS path %s %s %s",
+					stat.Name, stat.Tpgt[0].Name, stat.Tpgt[0].Luns[0].Name)
+				t.Errorf("%v", err)
+			}
+			if !reflect.DeepEqual(readTests[i].read, readMB) {
+				t.Errorf("unexpected iSCSI read data :\nwant:\n%v\nhave:\n%v", readTests[i].read, readMB)
+			}
+			if !reflect.DeepEqual(readTests[i].write, writeMB) {
+				t.Errorf("unexpected iSCSI write data :\nwant:\n%v\nhave:\n%v", readTests[i].write, writeMB)
+			}
+			if !reflect.DeepEqual(readTests[i].iops, iops) {
+				t.Errorf("unexpected iSCSI iops data :\nwant:\n%v\nhave:\n%v", readTests[i].iops, iops)
+			}
+			if stat.Tpgt[0].Luns[0].Backstore == "rd_mcp" {
+				have_rdmcp := new(iscsi.RDMCP)
+				have_rdmcp, err := have_rdmcp.GetRDMCPPath(filepath.Join("../fixtures/sys/kernel/config", iscsi.TargetCore), "119", "ramdisk_lio_1G")
+				if err != nil {
+					t.Errorf("fail rdmcp error %v", err)
+				}
+				// Name ObjectName
+				want_rdmcp := &iscsi.RDMCP{"rd_mcp_" + stat.Tpgt[0].Luns[0].TypeNumber, stat.Tpgt[0].Luns[0].ObjectName}
+
+				if !reflect.DeepEqual(want_rdmcp, have_rdmcp) {
+					t.Errorf("unexpected rdmcp data :\nwant:\n%v\nhave:\n%v", want_rdmcp, have_rdmcp)
+				}
+			} else if stat.Tpgt[0].Luns[0].Backstore == "iblock" {
+				have_iblock := new(iscsi.IBLOCK)
+				have_iblock, err = have_iblock.GetIblockUdev(filepath.Join("../fixtures/sys/kernel/config", iscsi.TargetCore), "0", "block_lio_rbd1")
+				if err != nil {
+					t.Errorf("fail iblock error %v", err)
+				}
+				// Name Bnumber ObjectName Iblock
+				want_iblock := &iscsi.IBLOCK{"iblock_" + stat.Tpgt[0].Luns[0].TypeNumber, stat.Tpgt[0].Luns[0].TypeNumber, stat.Tpgt[0].Luns[0].ObjectName, "/dev/rbd1"}
+				if !reflect.DeepEqual(want_iblock, have_iblock) {
+					t.Errorf("unexpected iblock data :\nwant:\n%v\nhave:\n%v", want_iblock, have_iblock)
+				}
+			} else if stat.Tpgt[0].Luns[0].Backstore == "fileio" {
+				have_fileio := new(iscsi.FILEIO)
+				have_fileio, err = have_fileio.GetFileioUdev(filepath.Join("../fixtures/sys/kernel/config", iscsi.TargetCore), "1", "file_lio_1G")
+				if err != nil {
+					t.Errorf("fail fileio error %v", err)
+				}
+				// Name, Fnumber, ObjectName, Filename
+				want_fileio := &iscsi.FILEIO{"fileio_" + stat.Tpgt[0].Luns[0].TypeNumber, stat.Tpgt[0].Luns[0].TypeNumber, "file_lio_1G", "/home/iscsi/file_back_1G"}
+				if !reflect.DeepEqual(want_fileio, have_fileio) {
+					t.Errorf("unexpected fileio data :\nwant:\n%v\nhave:\n%v", want_fileio, have_fileio)
+				}
+			} else if stat.Tpgt[0].Luns[0].Backstore == "rbd" {
+				have_rbd := new(iscsi.RBD)
+				have_rbd, err = have_rbd.GetRBDMatch("../fixtures/sys", "0", "iscsi-images-demo")
+				if err != nil {
+					t.Errorf("fail rbd error %v", err)
+				}
+				// Name, Rnumber, Pool, Image
+				want_rbd := &iscsi.RBD{"rbd_" + stat.Tpgt[0].Luns[0].TypeNumber, stat.Tpgt[0].Luns[0].TypeNumber, "iscsi-images", "demo"}
+				if !reflect.DeepEqual(want_rbd, have_rbd) {
+					t.Errorf("unexpected fileio data :\nwant:\n%v\nhave:\n%v", want_rbd, have_rbd)
+				}
+			}
+		}
+	}
+}
diff --git a/mdstat_test.go b/mdstat_test.go
index 99a5adf..f2d2648 100644
--- a/mdstat_test.go
+++ b/mdstat_test.go
@@ -13,24 +13,33 @@
 
 package procfs
 
-import (
-	"testing"
-)
+import "testing"
+
+func TestFS_MDStat(t *testing.T) {
+	fs := getProcFixtures(t)
+	mdStats, err := fs.MDStat()
 
-func TestMDStat(t *testing.T) {
-	mdStats, err := getProcFixtures(t).MDStat()
 	if err != nil {
 		t.Fatalf("parsing of reference-file failed entirely: %s", err)
 	}
 
 	refs := map[string]MDStat{
-		"md3":   {"md3", "active", 8, 8, 5853468288, 5853468288},
-		"md127": {"md127", "active", 2, 2, 312319552, 312319552},
-		"md0":   {"md0", "active", 2, 2, 248896, 248896},
-		"md4":   {"md4", "inactive", 2, 2, 4883648, 4883648},
-		"md6":   {"md6", "active", 1, 2, 195310144, 16775552},
-		"md8":   {"md8", "active", 2, 2, 195310144, 16775552},
-		"md7":   {"md7", "active", 3, 4, 7813735424, 7813735424},
+		"md127": {Name: "md127", ActivityState: "active", DisksActive: 2, DisksTotal: 2, DisksFailed: 0, DisksSpare: 0, BlocksTotal: 312319552, BlocksSynced: 312319552},
+		"md0":   {Name: "md0", ActivityState: "active", DisksActive: 2, DisksTotal: 2, DisksFailed: 0, DisksSpare: 0, BlocksTotal: 248896, BlocksSynced: 248896},
+		"md4":   {Name: "md4", ActivityState: "inactive", DisksActive: 0, DisksTotal: 0, DisksFailed: 1, DisksSpare: 1, BlocksTotal: 4883648, BlocksSynced: 4883648},
+		"md6":   {Name: "md6", ActivityState: "recovering", DisksActive: 1, DisksTotal: 2, DisksFailed: 1, DisksSpare: 1, BlocksTotal: 195310144, BlocksSynced: 16775552},
+		"md3":   {Name: "md3", ActivityState: "active", DisksActive: 8, DisksTotal: 8, DisksFailed: 0, DisksSpare: 2, BlocksTotal: 5853468288, BlocksSynced: 5853468288},
+		"md8":   {Name: "md8", ActivityState: "resyncing", DisksActive: 2, DisksTotal: 2, DisksFailed: 0, DisksSpare: 2, BlocksTotal: 195310144, BlocksSynced: 16775552},
+		"md7":   {Name: "md7", ActivityState: "active", DisksActive: 3, DisksTotal: 4, DisksFailed: 1, DisksSpare: 0, BlocksTotal: 7813735424, BlocksSynced: 7813735424},
+		"md9":   {Name: "md9", ActivityState: "resyncing", DisksActive: 4, DisksTotal: 4, DisksSpare: 1, DisksFailed: 2, BlocksTotal: 523968, BlocksSynced: 0},
+		"md10":  {Name: "md10", ActivityState: "active", DisksActive: 2, DisksTotal: 2, DisksFailed: 0, DisksSpare: 0, BlocksTotal: 314159265, BlocksSynced: 314159265},
+		"md11":  {Name: "md11", ActivityState: "resyncing", DisksActive: 2, DisksTotal: 2, DisksFailed: 1, DisksSpare: 2, BlocksTotal: 4190208, BlocksSynced: 0},
+		"md12":  {Name: "md12", ActivityState: "active", DisksActive: 2, DisksTotal: 2, DisksSpare: 0, DisksFailed: 0, BlocksTotal: 3886394368, BlocksSynced: 3886394368},
+		"md120": {Name: "md120", ActivityState: "active", DisksActive: 2, DisksTotal: 2, DisksFailed: 0, DisksSpare: 0, BlocksTotal: 2095104, BlocksSynced: 2095104},
+		"md126": {Name: "md126", ActivityState: "active", DisksActive: 2, DisksTotal: 2, DisksFailed: 0, DisksSpare: 0, BlocksTotal: 1855870976, BlocksSynced: 1855870976},
+		"md219": {Name: "md219", ActivityState: "inactive", DisksTotal: 0, DisksFailed: 0, DisksActive: 0, DisksSpare: 3, BlocksTotal: 7932, BlocksSynced: 7932},
+		"md00":  {Name: "md00", ActivityState: "active", DisksActive: 1, DisksTotal: 1, DisksFailed: 0, DisksSpare: 0, BlocksTotal: 4186624, BlocksSynced: 4186624},
+		"md101": {Name: "md101", ActivityState: "active", DisksActive: 3, DisksTotal: 3, DisksFailed: 0, DisksSpare: 0, BlocksTotal: 322560, BlocksSynced: 322560},
 	}
 
 	if want, have := len(refs), len(mdStats); want != have {
@@ -41,4 +50,20 @@ func TestMDStat(t *testing.T) {
 			t.Errorf("%s: want %v, have %v", md.Name, want, have)
 		}
 	}
+
+}
+
+func TestInvalidMdstat(t *testing.T) {
+	invalidMount := []byte(`
+Personalities : [invalid]
+md3 : invalid
+      314159265 blocks 64k chunks
+
+unused devices: <none>
+`)
+
+	_, err := parseMDStat(invalidMount)
+	if err == nil {
+		t.Fatalf("parsing of invalid reference file did not find any errors")
+	}
 }
diff --git a/mountinfo_test.go b/mountinfo_test.go
new file mode 100644
index 0000000..3565979
--- /dev/null
+++ b/mountinfo_test.go
@@ -0,0 +1,135 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package procfs
+
+import (
+	"reflect"
+	"testing"
+)
+
+func TestMountInfo(t *testing.T) {
+	tests := []struct {
+		name    string
+		s       string
+		mount   *MountInfo
+		invalid bool
+	}{
+		{
+			name:    "Regular sysfs mounted at /sys",
+			s:       "16 21 0:16 / /sys rw,nosuid,nodev,noexec,relatime shared:7 - sysfs sysfs rw",
+			invalid: false,
+			mount: &MountInfo{
+				MountId:        16,
+				ParentId:       21,
+				MajorMinorVer:  "0:16",
+				Root:           "/",
+				MountPoint:     "/sys",
+				Options:        map[string]string{"rw": "", "nosuid": "", "nodev": "", "noexec": "", "relatime": ""},
+				OptionalFields: map[string]string{"shared": "7"},
+				FSType:         "sysfs",
+				Source:         "sysfs",
+				SuperOptions:   map[string]string{"rw": ""},
+			},
+		},
+		{
+			name:    "Not enough information",
+			s:       "hello",
+			invalid: true,
+		},
+		{
+			name: "Tmpfs mounted at /run",
+			s:    "225 20 0:39 / /run/user/112 rw,nosuid,nodev,relatime shared:177 - tmpfs tmpfs rw,size=405096k,mode=700,uid=112,gid=116",
+			mount: &MountInfo{
+				MountId:        225,
+				ParentId:       20,
+				MajorMinorVer:  "0:39",
+				Root:           "/",
+				MountPoint:     "/run/user/112",
+				Options:        map[string]string{"rw": "", "nosuid": "", "nodev": "", "relatime": ""},
+				OptionalFields: map[string]string{"shared": "177"},
+				FSType:         "tmpfs",
+				Source:         "tmpfs",
+				SuperOptions:   map[string]string{"rw": "", "size": "405096k", "mode": "700", "uid": "112", "gid": "116"},
+			},
+			invalid: false,
+		},
+		{
+			name: "Tmpfs mounted at /run, but no optional values",
+			s:    "225 20 0:39 / /run/user/112 rw,nosuid,nodev,relatime  - tmpfs tmpfs rw,size=405096k,mode=700,uid=112,gid=116",
+			mount: &MountInfo{
+				MountId:        225,
+				ParentId:       20,
+				MajorMinorVer:  "0:39",
+				Root:           "/",
+				MountPoint:     "/run/user/112",
+				Options:        map[string]string{"rw": "", "nosuid": "", "nodev": "", "relatime": ""},
+				OptionalFields: nil,
+				FSType:         "tmpfs",
+				Source:         "tmpfs",
+				SuperOptions:   map[string]string{"rw": "", "size": "405096k", "mode": "700", "uid": "112", "gid": "116"},
+			},
+			invalid: false,
+		},
+		{
+			name: "Tmpfs mounted at /run, with multiple optional values",
+			s:    "225 20 0:39 / /run/user/112 rw,nosuid,nodev,relatime shared:177 master:8 - tmpfs tmpfs rw,size=405096k,mode=700,uid=112,gid=116",
+			mount: &MountInfo{
+				MountId:        225,
+				ParentId:       20,
+				MajorMinorVer:  "0:39",
+				Root:           "/",
+				MountPoint:     "/run/user/112",
+				Options:        map[string]string{"rw": "", "nosuid": "", "nodev": "", "relatime": ""},
+				OptionalFields: map[string]string{"shared": "177", "master": "8"},
+				FSType:         "tmpfs",
+				Source:         "tmpfs",
+				SuperOptions:   map[string]string{"rw": "", "size": "405096k", "mode": "700", "uid": "112", "gid": "116"},
+			},
+			invalid: false,
+		},
+		{
+			name: "Tmpfs mounted at /run, with a mixture of valid and invalid optional values",
+			s:    "225 20 0:39 / /run/user/112 rw,nosuid,nodev,relatime shared:177 master:8 foo:bar - tmpfs tmpfs rw,size=405096k,mode=700,uid=112,gid=116",
+			mount: &MountInfo{
+				MountId:        225,
+				ParentId:       20,
+				MajorMinorVer:  "0:39",
+				Root:           "/",
+				MountPoint:     "/run/user/112",
+				Options:        map[string]string{"rw": "", "nosuid": "", "nodev": "", "relatime": ""},
+				OptionalFields: map[string]string{"shared": "177", "master": "8"},
+				FSType:         "tmpfs",
+				Source:         "tmpfs",
+				SuperOptions:   map[string]string{"rw": "", "size": "405096k", "mode": "700", "uid": "112", "gid": "116"},
+			},
+			invalid: false,
+		},
+	}
+
+	for i, test := range tests {
+		t.Logf("[%02d] test %q", i, test.name)
+
+		mount, err := parseMountInfoString(test.s)
+
+		if test.invalid && err == nil {
+			t.Error("expected an error, but none occurred")
+		}
+		if !test.invalid && err != nil {
+			t.Errorf("unexpected error: %v", err)
+		}
+
+		if want, have := test.mount, mount; !reflect.DeepEqual(want, have) {
+			t.Errorf("mounts:\nwant:\n%+v\nhave:\n%+v", want, have)
+		}
+	}
+}
diff --git a/proc_environ_test.go b/proc_environ_test.go
new file mode 100644
index 0000000..235f888
--- /dev/null
+++ b/proc_environ_test.go
@@ -0,0 +1,47 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package procfs
+
+import "testing"
+
+func TestProcEnviron(t *testing.T) {
+	p, err := getProcFixtures(t).Proc(26231)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	environments, err := p.Environ()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	expectedEnvironments := []string{
+		"PATH=/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+		"HOSTNAME=cd24e11f73a5",
+		"TERM=xterm",
+		"GOLANG_VERSION=1.12.5",
+		"GOPATH=/go",
+		"HOME=/root",
+	}
+
+	if want, have := len(expectedEnvironments), len(environments); want != have {
+		t.Errorf("want %d parsed environments, have %d", want, have)
+	}
+
+	for i, environment := range environments {
+		if want, have := expectedEnvironments[i], environment; want != have {
+			t.Errorf("%d: want %v, have %v", i, want, have)
+		}
+	}
+}