You are not logged in.
This is a report on my attempts to recover info from a sd-card before I re-format it. Here is how the story begins:
A 64GB SD-Card was used within a SmartPrime7 (VFD-600) Android-6 mobile phone
June 08: The (non-replaceable) battery in that SmartPrime7 died
June 09: A Blackview A52 Android-12 mobile phone was obtained as a replacement
June 10: Vodaphone transferred both SIMM + SD-Card to the A52 for me
The A52 Contact list was easily populated from the SIMM during setup
The A52 wanted to re-format the SD-Card; could any data be extracted first?
The Card was plugged into a SD/MMC cardreader and then into a USB socket in my Desktop computer running Chimaera 4
The reader LED lit-up, but it did not auto-mount. I now entered into a blizzard of DDG-searches to discover how-to mount these partitions (and why they did not auto-mount).
Consulting man mount, the process at it’s simplest is the command “sudo mount device mountpoint”, where:–
‘sudo’ == in most cases this admin command must be performed as root
‘mount’ == the Devuan command to mount the device
‘device’ == the name given by Devuan to the physical device that contains a filesystem (fs)
‘mountpoint’ == a directory that pre-exists within the existing Devuan fs
There are 3 wrinkles to this command in this case:–
It usually is able to auto-detect the fs type (but not here), so a `-t fstype` will be required
‘device’ can often change on restart, meaning that a `-L Label` or `-U UUID` would be required if repetitively loaded
Any existing files within ‘mountpoint’ will no longer be accessible when a device is mounted there.
dmesg is normally the first port of call if it has just been inserted/attached, and fdisk is next, with two levels of detail available (both below). Neither helps, and an attempt at mount simply underlines the error. The fs can be found using fsck -N (no-action, and it simply reports the fs). That is fsck.ext2 which means that the fs is ‘ext2’. We try a mount with that & it fails. So, try to find alternate superblocks with mke2fs -n /dev/sdc1, and try to use an alternate superblock to fix the fs, but nothing works (the disk is in the toilet).
We need to try more desperate measures, and turn to testdisk (available from the standard distribution) (see next post).
$ sudo dmesg | tail
[ 832.195604] scsi host3: usb-storage 1-1.3:1.0
[ 833.219061] scsi 3:0:0:0: Direct-Access Generic- SD/MMC 1.00 PQ: 0 ANSI: 0 CCS
[ 833.219877] sd 3:0:0:0: Attached scsi generic sg3 type 0
[ 834.000163] sd 3:0:0:0: [sdc] 124735488 512-byte logical blocks: (63.9 GB/59.5 GiB)
[ 834.001152] sd 3:0:0:0: [sdc] Write Protect is off
[ 834.001164] sd 3:0:0:0: [sdc] Mode Sense: 03 00 00 00
[ 834.002150] sd 3:0:0:0: [sdc] No Caching mode page found
[ 834.002160] sd 3:0:0:0: [sdc] Assuming drive cache: write through
[ 834.020198] sdc: sdc1 sdc2
[ 834.020700] sd 3:0:0:0: [sdc] Attached SCSI removable disk
$ sudo fdisk -l
[sudo] password for alexk:
…
Disk /dev/sdc: 59.48 GiB, 63864569856 bytes, 124735488 sectors
Disk model: SD/MMC
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 1D4B1382-C639-443D-9D04-E4C1D8ADB90B
Device Start End Sectors Size Type
/dev/sdc1 2048 34815 32768 16M unknown
/dev/sdc2 34816 124735454 124700639 59.5G unknown
$ sudo fdisk -x
…
Disk /dev/sdc: 59.48 GiB, 63864569856 bytes, 124735488 sectors
Disk model: SD/MMC
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 1D4B1382-C639-443D-9D04-E4C1D8ADB90B
First LBA: 34
Last LBA: 124735454
Alternative LBA: 124735487
Partition entries LBA: 2
Allocated partition entries: 128
Device Start End Sectors Type-UUID UUID Name Attrs
/dev/sdc1 2048 34815 32768 19A710A2-B3CA-11E4-B026-10604B889DCF 1993C6FB-24DB-4FC6-9452-352139178BD0 android_meta
/dev/sdc2 34816 124735454 124700639 193D1EA4-B3CA-11E4-B075-10604B889DCF CFDE466A-A074-7AC9-0A48-A766192AD94A android_expand
$ sudo fsck -N /dev/sdc1
fsck from util-linux 2.36.1
[/sbin/fsck.ext2 (1) -- /dev/sdc1] fsck.ext2 /dev/sdc1
$ sudo fsck -N /dev/sdc2
fsck from util-linux 2.36.1
[/sbin/fsck.ext2 (1) -- /dev/sdc2] fsck.ext2 /dev/sdc2
$ mkdir -p ~/TMP/SDC1
$ mkdir -p ~/TMP/SDC2
$ sudo mount /dev/sdc1 ~/TMP/SDC1
[sudo] password for alexk:
mount: /home/alexk/TMP/SDC1: wrong fs type, bad option, bad superblock on /dev/sdc1, missing codepage or helper program, or other error.
$ sudo mount -t ext2 /dev/sdc1 ~/TMP/SDC1
mount: /home/alexk/TMP/SDC1: wrong fs type, bad option, bad superblock on /dev/sdc1, missing codepage or helper program, or other error.
$ sudo fsck.ext2 /dev/sdc1 -v
e2fsck 1.46.2 (28-Feb-2021)
ext2fs_open2: Bad magic number in super-block
fsck.ext2: Superblock invalid, trying backup blocks...
fsck.ext2: Bad magic number in super-block while trying to open /dev/sdc1
The superblock could not be read or does not describe a valid ext2/ext3/ext4
filesystem. If the device is valid and it really contains an ext2/ext3/ext4
filesystem (and not swap or ufs or something else), then the superblock
is corrupt, and you might try running e2fsck with an alternate superblock:
e2fsck -b 8193 <device>
or
e2fsck -b 32768 <device>
$ sudo mke2fs -n /dev/sdc1
mke2fs 1.46.2 (28-Feb-2021)
Creating filesystem with 16384 1k blocks and 4096 inodes
Filesystem UUID: 4fce5c0e-d330-4406-b7bf-3935e5161dac
Superblock backups stored on blocks:
8193
$ sudo e2fsck -b 8193 /dev/sdc1
e2fsck 1.46.2 (28-Feb-2021)
e2fsck: Invalid argument while trying to open /dev/sdc1
The superblock could not be read or does not describe a valid ext2/ext3/ext4
filesystem. If the device is valid and it really contains an ext2/ext3/ext4
filesystem (and not swap or ufs or something else), then the superblock
is corrupt, and you might try running e2fsck with an alternate superblock:
e2fsck -b 8193 <device>
or
e2fsck -b 32768 <device>
$ sudo e2fsck -b 32768 /dev/sdc1
e2fsck 1.46.2 (28-Feb-2021)
e2fsck: Invalid argument while trying to open /dev/sdc1
The superblock could not be read or does not describe a valid ext2/ext3/ext4
filesystem. If the device is valid and it really contains an ext2/ext3/ext4
filesystem (and not swap or ufs or something else), then the superblock
is corrupt, and you might try running e2fsck with an alternate superblock:
e2fsck -b 8193 <device>
or
e2fsck -b 32768 <device>
cd ~/TMP
$ sudo testdisk /list
[sudo] password for alexk:
TestDisk 7.1, Data Recovery Utility, July 2019
Christophe GRENIER <grenier@cgsecurity.org>
https://www.cgsecurity.org
Please wait...
Disk /dev/sda - 1000 GB / 931 GiB - CHS 121601 255 63
Sector size:512
Model: WDC WD10EZEX-08Y20A0, S/N:WD-WCC3F1TLE7XN, FW:02.01A02
Disk /dev/sdc - 63 GB / 59 GiB - CHS 60906 64 32
Sector size:512
Model: Generic- SD/MMC, FW:1.00
Disk /dev/sda - 1000 GB / 931 GiB - CHS 121601 255 63
Partition Start End Size in sectors
1 P EFI System 2048 1050623 1048576
2 P Linux filesys. data 1050624 1937960959 1936910336
3 P Linux Swap 1937960960 1953523711 15562752
Disk /dev/sdc - 63 GB / 59 GiB - CHS 60906 64 32
Partition Start End Size in sectors
1 P Unknown 2048 34815 32768 [android_meta]
2 P Unknown 34816 124735454 124700639 [android_expand]
The next post will run through how-to use testdisk (and I hope that it works).
Offline
Firstly It might be a help to use dd to make an image file of the card, and then work with that. Like
# dd if=/dev/sdc of=diskimage bs=100M
Being Android sdcard the setup for the partitions might be FAT32 (or vfat?) with an ext2 (or perhaps ext4) within.
And it might be encrypted.
EDIT: attempt to clarify
Offline
Hi Ralph.
Yup, did that before starting! It is a 59.5 GiB file called "sdc64gb.img". It is also worth pointing out that testdisk can also save an image of the partition(s) to disk before trying to rewrite.
partitions might be FAT32 (or vfat?) with an ext2 (or perhaps ext4) within
I'm spending yet another hour analysing the wretched cylinders to try that one. I previously set the fstype to 'ext2' & tried to view files & it said "none … damaged".
I'm pretty sure that I'm going to have to let the A52 reformat it & forget any existing files. I've got a very old backup on disk, but that is all.
Offline
This post is about using testdisk
When launched all by itself, testdisk launches into it’s own text-window with highlit-options to choose from. We select Create:–
TestDisk 7.1, Data Recovery Utility, July 2019
Christophe GRENIER <grenier@cgsecurity.org>
https://www.cgsecurity.org
TestDisk is free data recovery software designed to help recover lost
partitions and/or make non-booting disks bootable again when these symptoms
are caused by faulty software, certain types of viruses or human error.
It can also be used to repair some filesystem errors.
Information gathered during TestDisk use can be recorded for later
review. If you choose to create the text file, testdisk.log , it
will contain TestDisk options, technical information and various
outputs; including any folder/file names TestDisk was used to find and
list onscreen.
Use arrow keys to select, then press Enter key:
>[ Create ] Create a new log file
[ Append ] Append information to log file
[ No Log ] Don't record anything
Now /dev/sdc is chosen …
!!! REMEMBER !!! This is running as root. This is the perfect opportunity to shaft a working system. Be very careful!
:–
TestDisk 7.1, Data Recovery Utility, July 2019
Christophe GRENIER <grenier@cgsecurity.org>
https://www.cgsecurity.org
TestDisk is free software, and
comes with ABSOLUTELY NO WARRANTY.
Select a media (use Arrow keys, then press Enter):
Disk /dev/sda - 1000 GB / 931 GiB - WDC WD10EZEX-08Y20A0
>Disk /dev/sdc - 63 GB / 59 GiB - Generic- SD/MMC
>[Proceed ] [ Quit ]
Note: Disk capacity must be correctly detected for a successful recovery.
If a disk listed above has an incorrect size, check HD jumper settings and BIOS
detection, and install the latest OS patches and disk drivers.
Now to select the partition (fingers crossed):–
TestDisk 7.1, Data Recovery Utility, July 2019
Christophe GRENIER <grenier@cgsecurity.org>
https://www.cgsecurity.org
Disk /dev/sdc - 63 GB / 59 GiB - Generic- SD/MMC
Please select the partition table type, press Enter when done.
[Intel ] Intel/PC partition
>[EFI GPT] EFI GPT partition map (Mac i386, some x86_64...)
[Humax ] Humax partition table
[Mac ] Apple partition map (legacy)
[None ] Non partitioned media
[Sun ] Sun Solaris partition
[XBox ] XBox partition
[Return ] Return to disk selection
Hint: EFI GPT partition table type has been detected.
Note: Do NOT select 'None' for media with only a single partition. It's very
rare for a disk to be 'Non-partitioned'.
Now Analyse to try to check stuff
([ Advanced ] Filesystem Utils is used to undelete files, and is *very* effective) (no use here since files cannot be detected):–
TestDisk 7.1, Data Recovery Utility, July 2019
Christophe GRENIER <grenier@cgsecurity.org>
https://www.cgsecurity.org
Disk /dev/sdc - 63 GB / 59 GiB - Generic- SD/MMC
CHS 60906 64 32 - sector size=512
>[ Analyse ] Analyse current partition structure and search for lost partitions
[ Advanced ] Filesystem Utils
[ Geometry ] Change disk geometry
[ Options ] Modify options
[ Quit ] Return to disk selection
Note: Correct disk geometry is required for a successful recovery. 'Analyse'
process may give some warnings if it thinks the logical geometry is mismatched.
Choose default option (this takes a heck of a long while as it ploughs through every cylinder) (~1hr for 64G disk):–
TestDisk 7.1, Data Recovery Utility, July 2019
Christophe GRENIER <grenier@cgsecurity.org>
https://www.cgsecurity.org
Disk /dev/sdc - 63 GB / 59 GiB - CHS 60906 64 32
Current partition structure:
Partition Start End Size in sectors
1 P Unknown 2048 34815 32768 [android_meta]
2 P Unknown 34816 124735454 124700639 [android_expand]
P=Primary D=Deleted
>[Quick Search] [ Backup ]
Try to locate partition
After ~1 hour the next screen is shown:–
TestDisk 7.1, Data Recovery Utility, July 2019
Christophe GRENIER <grenier@cgsecurity.org>
https://www.cgsecurity.org
Disk /dev/sdc - 63 GB / 59 GiB - CHS 60906 64 32
Partition Start End Size in sectors
Keys A: add partition, L: load backup, Enter: to continue
Pressing 'A' then gives this next screen (this is NOT what a successful fix looks like):–
TestDisk 7.1, Data Recovery Utility, July 2019
Christophe GRENIER <grenier@cgsecurity.org>
https://www.cgsecurity.org
Disk /dev/sdc - 63 GB / 59 GiB - CHS 60906 64 32
Unknown 1 124735487 124735487
>[Sector] [Sector] [ Type ] [ Done ]
Change starting sector
I entered two partitions using the former values, each with a fstype of EFI/ext2, but then trying to view files gave a "must be damaged" result. Entering a fstype EFI/unknown gave the following (no files can be viewed):–
TestDisk 7.1, Data Recovery Utility, July 2019
Christophe GRENIER <grenier@cgsecurity.org>
https://www.cgsecurity.org
Disk /dev/sdc - 63 GB / 59 GiB - CHS 60906 64 32
Partition Start End Size in sectors
P EFI System 2048 34815 32768
>P EFI System 34816 124735454 124700639
Structure: Ok. Use Up/Down Arrow keys to select partition.
Use Left/Right Arrow keys to CHANGE partition characteristics:
P=Primary D=Deleted
Keys A: add partition, L: load backup, T: change type,
Enter: to continue
63 GB / 59 GiB
Pressing Enter now gave:
TestDisk 7.1, Data Recovery Utility, July 2019
Christophe GRENIER <grenier@cgsecurity.org>
https://www.cgsecurity.org
Disk /dev/sdc - 63 GB / 59 GiB - CHS 60906 64 32
Partition Start End Size in sectors
1 P EFI System 2048 34815 32768
2 P EFI System 34816 124735454 124700639
[ Quit ] >[ Return ] [ Write ]
Return to partition selection
Choosing Write gave:
TestDisk 7.1, Data Recovery Utility, July 2019
Christophe GRENIER <grenier@cgsecurity.org>
https://www.cgsecurity.org
Write partition table, confirm ? (Y/N)
Ah well, the disk is toast anyway, so I said 'Y':
TestDisk 7.1, Data Recovery Utility, July 2019
Christophe GRENIER <grenier@cgsecurity.org>
https://www.cgsecurity.org
You will have to reboot for the change to take effect.
>[Ok]
Chose Quit
TestDisk 7.1, Data Recovery Utility, July 2019
Christophe GRENIER <grenier@cgsecurity.org>
https://www.cgsecurity.org
Disk /dev/sdc - 63 GB / 59 GiB - Generic- SD/MMC
CHS 60906 64 32 - sector size=512
[ Analyse ] Analyse current partition structure and search for lost partitions
[ Advanced ] Filesystem Utils
[ Geometry ] Change disk geometry
[ Options ] Modify options
>[ Quit ] Return to disk selection
Note: Correct disk geometry is required for a successful recovery. 'Analyse'
process may give some warnings if it thinks the logical geometry is mismatched.
and Quit:
TestDisk 7.1, Data Recovery Utility, July 2019
Christophe GRENIER <grenier@cgsecurity.org>
https://www.cgsecurity.org
TestDisk is free software, and
comes with ABSOLUTELY NO WARRANTY.
Select a media (use Arrow keys, then press Enter):
Disk /dev/sda - 1000 GB / 931 GiB - WDC WD10EZEX-08Y20A0
>Disk /dev/sdc - 63 GB / 59 GiB - Generic- SD/MMC
[Proceed ] >[ Quit ]
Quit program
Note: Disk capacity must be correctly detected for a successful recovery.
If a disk listed above has an incorrect size, check HD jumper settings and BIOS
detection, and install the latest OS patches and disk drivers.
Offline
Now tried selecting FAT32 for both partitions, leading to this screen:
TestDisk 7.1, Data Recovery Utility, July 2019
Christophe GRENIER <grenier@cgsecurity.org>
https://www.cgsecurity.org
Disk /dev/sdc - 63 GB / 59 GiB - CHS 60906 64 32
Partition Start End Size in sectors
P EFI System 2048 34815 32768
>P EFI System 34816 124735454 124700639
Structure: Ok. Use Up/Down Arrow keys to select partition.
Use Left/Right Arrow keys to CHANGE partition characteristics:
P=Primary D=Deleted
Keys A: add partition, L: load backup, T: change type, P: list files,
Enter: to continue
63 GB / 59 GiB
… but no joy. Choosing P to list files gave the following screen:–
TestDisk 7.1, Data Recovery Utility, July 2019
Christophe GRENIER <grenier@cgsecurity.org>
https://www.cgsecurity.org
P EFI System 2048 34815 32768
Directory /
No file found, filesystem may be damaged.
Use Right to change directory, h to hide deleted files
q to quit, : to select the current file, a to select all files
C to copy the selected files, c to copy the current file
I'm going to leave this now. testdisk cannot find the fstype, and there are too many options to try to guess.
Offline
A little extra:
Checking the disk-image ‘sdc64gb.img’ that I took using dd at 02:59 this morning shows the reason that testdisk reports it as an EFI partition (look at 00000200):–
$ la
total 62367844
-rw-r--r-- 1 alexk alexk 63864569856 Jun 11 02:59 sdc64gb.img
alexk@ng3:~/TMP$ hexdump -C sdc64gb.img | less
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
000001c0 02 00 ee ff ff ff 01 00 00 00 ff 4f 6f 07 00 00 |...........Oo...|
000001d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
00000200 45 46 49 20 50 41 52 54 00 00 01 00 5c 00 00 00 |EFI PART....\...|
00000210 cb cc 43 cd 00 00 00 00 01 00 00 00 00 00 00 00 |..C.............|
00000220 ff 4f 6f 07 00 00 00 00 22 00 00 00 00 00 00 00 |.Oo.....".......|
00000230 de 4f 6f 07 00 00 00 00 82 13 4b 1d 39 c6 3d 44 |.Oo.......K.9.=D|
00000240 9d 04 e4 c1 d8 ad b9 0b 02 00 00 00 00 00 00 00 |................|
00000250 80 00 00 00 80 00 00 00 98 a4 38 f4 00 00 00 00 |..........8.....|
00000260 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000400 a2 10 a7 19 ca b3 e4 11 b0 26 10 60 4b 88 9d cf |.........&.`K...|
00000410 fb c6 93 19 db 24 c6 4f 94 52 35 21 39 17 8b d0 |.....$.O.R5!9...|
00000420 00 08 00 00 00 00 00 00 ff 87 00 00 00 00 00 00 |................|
00000430 00 00 00 00 00 00 00 00 61 00 6e 00 64 00 72 00 |........a.n.d.r.|
00000440 6f 00 69 00 64 00 5f 00 6d 00 65 00 74 00 61 00 |o.i.d._.m.e.t.a.|
00000450 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000480 a4 1e 3d 19 ca b3 e4 11 b0 75 10 60 4b 88 9d cf |..=......u.`K...|
00000490 6a 46 de cf 74 a0 c9 7a 0a 48 a7 66 19 2a d9 4a |jF..t..z.H.f.*.J|
000004a0 00 88 00 00 00 00 00 00 de 4f 6f 07 00 00 00 00 |.........Oo.....|
000004b0 00 00 00 00 00 00 00 00 61 00 6e 00 64 00 72 00 |........a.n.d.r.|
000004c0 6f 00 69 00 64 00 5f 00 65 00 78 00 70 00 61 00 |o.i.d._.e.x.p.a.|
000004d0 6e 00 64 00 00 00 00 00 00 00 00 00 00 00 00 00 |n.d.............|
000004e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
It's a damn shame that it could not detect the fstype.
Offline
Maybe clip out the two partitions into their own image files, and see if the file command tells... EFI partitions would be fat... with mtools you'd do
mdir -i part1.img -a
to see the files in that.
EDIT: I guess you know, but the clipping out would be dd commands like
$ dd if=sdc64gb.img of=part1.img skip=2048 count=32768
$ dd if=sdc64gb.img of=part2.img skip=34816 count=124700639
Offline
If this was used as "expanded storage" on a modern android device (and I expect it was by the [android_expand] partition type), it's almost certainly encrypted.
I'm not familiar with that particular device (nor do I remember in exactly which android version this changed), but encrypting user data storage has been the default for some time now, and unless you explicitly set up the device otherwise a card used for "expanded storage" qualifies.
If it was formatted as removable, it should just be a normal exFAT filesystem.
Last edited by steve_v (2023-06-12 03:00:25)
Once is happenstance. Twice is coincidence. Three times is enemy action. Four times is Official GNOME Policy.
Offline
OK, dd has finally finished & I can get a first look at both images.
(Life with this desktop is not as easy at this moment as it should be: the 1TB hdd is 97% full, which means that there is some serious clear-out to be done, and as I work HandBreak is ripping an ancient Blu-ray to disc which means 100% on the CPU, 6 hours to rip it, 2% swap, and as soon as swap initiates I notice that the performance of the whole system falls through the floor.)
part1.img seems to be composed entirely of null-bytes:
:~/TMP$ hexdump -C part1.img | head
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
01000000
part2.img is NOT an exFAT fs:
$ mdir -i part2.img -a
Unknown media type f5
$ hexdump -C part2.img | head
00000000 98 56 46 c4 a3 02 69 13 da 93 4f 6a fc 9b 87 72 |.VF...i...Oj...r|
00000010 0e 17 d8 67 56 d6 1f 53 1b 0f f4 b7 cd 4d c4 c1 |...gV..S.....M..|
00000020 75 27 9c a0 10 c7 43 3a a5 be 2a 08 63 5d 51 48 |u'....C:..*.c]QH|
00000030 24 37 00 54 38 66 ca 64 4f 9a 61 e7 e3 8c 63 9c |$7.T8f.dO.a...c.|
00000040 9b ca 08 17 86 78 f4 61 07 ae 54 9f ce da 2e 66 |.....x.a..T....f|
00000050 89 fe dd 59 27 fb c8 ad d4 11 1f 8d 31 3c 17 a7 |...Y'.......1<..|
00000060 1b bb 51 b8 74 8a b8 21 9f ec cc f4 32 74 2b 9a |..Q.t..!....2t+.|
00000070 c8 e1 e4 4d 16 e3 dd 88 6e e0 63 c7 3e 48 7c f9 |...M....n.c.>H|.|
00000080 db ce 24 6f 97 5a b9 62 8b 2e e4 e3 2a dd 95 c2 |..$o.Z.b....*...|
00000090 24 1d 8e a0 a2 15 f7 51 d9 4e 38 2f 92 a4 5c 17 |$......Q.N8/..\.|
Offline