เหตุใดการเมานท์จึงเกิดขึ้นกับไดเรกทอรีที่มีอยู่


52

ไดเรกทอรีที่มีอยู่เป็นสิ่งจำเป็นเป็นจุดเชื่อมต่อ

$ ls
$ sudo mount /dev/sdb2 ./datadisk
mount: mount point ./datadisk does not exist
$ mkdir datadisk
$ sudo mount /dev/sdb2 ./datadisk
$

ฉันพบว่ามันสับสนเพราะมันซ้อนทับเนื้อหาที่มีอยู่ของไดเรกทอรี มีสองเนื้อหาที่เป็นไปได้ของไดเรกทอรีจุดเชื่อมต่อซึ่งอาจได้รับการสลับโดยไม่คาดคิด (สำหรับผู้ใช้ที่ไม่ได้ทำการติดตั้ง)

เหตุใดจึงไม่mountเกิดขึ้นในไดเรกทอรีที่สร้างขึ้นใหม่ นี่คือวิธีที่ระบบปฏิบัติการกราฟิกแสดงสื่อบันทึกข้อมูลแบบถอดได้ มันจะชัดเจนหากติดตั้งไดเรกทอรี (มีอยู่) หรือไม่ได้ติดตั้ง (ไม่มีอยู่) ฉันค่อนข้างมั่นใจว่ามีเหตุผลที่ดี แต่ฉันยังไม่สามารถค้นพบได้


1
udisksctlหากคุณต้องการพฤติกรรมที่ใช้ ทำไมต้องใช้mount?
muru

1
เพราะมันเป็นหนทางของ Unix เพราะวิธีนี้มีความยืดหยุ่นมากกว่าและคุณสามารถเมานท์ได้ทุกที่ เนื่องจากการติดตั้งที่ใดก็ตามทำให้คุณสามารถขยายเซิร์ฟเวอร์ของคุณได้ตามต้องการเช่นรับดิสก์ใหม่สำหรับพาร์ติชันฐานข้อมูลย้ายข้อมูลในพาร์ติชัน DB ไปยังดิสก์ใหม่และติดตั้งในตำแหน่งที่ถูกต้องเพื่อให้ข้อมูล DB ที่จะเติบโตมากขึ้น
Rui F Ribeiro

8
ในฐานะที่เป็นบันทึกประวัติศาสตร์ก่อนที่ Windows และ LInux จะทำลายระบบปฏิบัติการอื่นทั้งหมดเป็นหลักมี บริษัท หนึ่งชื่อ Apollo พวกเขาเขียนระบบปฏิบัติการยูนิกซ์เหมือนกัน (ออกแบบดีกว่า Unix!) มันสร้างไดเร็กทอรีที่ NFS เอ็กซ์พอร์ตติดตั้งโดยอัตโนมัติ ที่จริงแล้วคุณไม่สามารถเมานต์ไดเรคทอรีที่มีอยู่แล้วได้ HP ซื้อ Apollo ทิ้งระบบปฏิบัติการทิ้งและใช้ CPU 64 บิตของ Apollo เป็น HP-PA ระบบการเรียกกระบวนการระยะไกลของอพอลโลกลายเป็น DCE ของ OSF ซึ่งเห็นได้ชัดว่าอาศัยอยู่ภายใน Windows การรู้คือการต่อสู้ครึ่งหนึ่ง!
Bruce Ediger

อย่างใดสิ่งนี้เกิดขึ้นในระบบ Ubuntu 14.04,3 ​​ของฉัน ฉันยังไม่ได้ตรวจสอบ เมื่อการ์ด SD ของฉันถูกเมาท์มันก็จบลงที่เส้นทางซึ่งไม่มีอะไรอยู่ข้างใต้ ถ้าฉันถอนติดตั้งและลองเมานต์กลับด้วยตนเองข้อผิดพลาดที่ฉันได้รับคือไม่มีไดเรกทอรีที่จุดเมานท์
Skaperen

2
@BruceEdiger better design than Unix![อ้างจำเป็น]
Ruslan

คำตอบ:


51

นี่เป็นกรณีของรายละเอียดการใช้งานที่รั่วไหลออกมา

ในระบบ UNIX ทุกไดเร็กทอรีประกอบด้วยรายการชื่อที่แม็พกับหมายเลขไอโหนด ไอโหนดเก็บเมตาดาต้าซึ่งบอกระบบว่าเป็นไฟล์ไดเรกทอรีอุปกรณ์พิเศษชื่อไปป์ ฯลฯ หากเป็นไฟล์หรือไดเรกทอรีมันยังบอกระบบว่าจะหาไฟล์หรือเนื้อหาของไดเร็กตอรี่ในดิสก์ได้ที่ใด inodes ส่วนใหญ่เป็นไฟล์หรือไดเรกทอรี -iตัวเลือกในการlsจะแสดงหมายเลขไอโหนด

การติดตั้งระบบไฟล์จะใช้ไดเร็กทอรี inode และตั้งค่าสถานะบนสำเนาในหน่วยความจำของเคอร์เนลเพื่อพูดว่า "จริง ๆ แล้วเมื่อค้นหาเนื้อหาของไดเรกทอรีนี้ให้ดูที่ระบบไฟล์อื่นแทน" (ดูสไลด์ 10 ของงานนำเสนอนี้ ) สิ่งนี้ค่อนข้างง่ายเนื่องจากกำลังเปลี่ยนรายการข้อมูลเดียว

ทำไมมันไม่สร้างรายการไดเรกทอรีให้คุณชี้ไปที่ไอโหนดใหม่แทน มีสองวิธีที่คุณสามารถใช้งานได้ซึ่งทั้งสองอย่างมีข้อเสีย หนึ่งคือการเขียนไดเรกทอรีใหม่ลงในระบบไฟล์ - แต่มันล้มเหลวถ้าระบบไฟล์อ่านได้อย่างเดียว! อีกรายการหนึ่งคือการเพิ่มทุกรายการในไดเรกทอรีรายการกระบวนการของ "พิเศษ" ที่ไม่ได้มี นี่เป็นเที่ยวยุ่งยิ่งและอาจก่อให้เกิดประสิทธิภาพเล็กน้อยในทุกการดำเนินการไฟล์

หากคุณต้องการจุดเชื่อมต่อที่สร้างขึ้นแบบไดนามิกautomountระบบสามารถทำได้ พิเศษ filesystems ไม่ใช่ดิสก์ยังสามารถสร้างไดเรกทอรีที่จะเช่นproc, sys, devfsและอื่น ๆ

แก้ไข: ดูคำตอบสำหรับสิ่งที่เกิดขึ้นเมื่อคุณ 'เมานต์' โฟลเดอร์ที่มีเนื้อหาอยู่


ยกเว้นว่ามันจะไม่ตั้งค่าสถานะในไอโหนด sudo mount --bind / /mnt ; ls /mnt/proc-> ว่างเปล่า ฉันสงสัยว่ามันทำงานอย่างไร
sourcejedi

การดำเนินการที่แน่นอนอยู่ในfs/namespace.cฉันคิดว่า; ฉันไม่คุ้นเคยกับแหล่งที่มาและไม่ต้องการใช้เวลานานเกินไปในการเจาะลึกถึงรายละเอียด "flag in inode" ที่ฉันได้รับจากการนำเสนอที่เชื่อมโยง
pjc50

2
@sourcejedi: bind mounts ผูกระบบแฟ้มที่คุณอ้างถึงเท่านั้น พวกเขาจะไม่ผูกระบบไฟล์อื่น ๆ ที่ติดตั้งไว้ซ้ำ ๆ นี่เป็นวิธีที่สะดวกในการค้นหาขยะที่ซ่อนโดยการติดตั้ง (เช่นถ้าสิ่งบางอย่างลงเอย FS รากใน/var/cacheบางเวลาที่/varล้มเหลวในการติดตั้ง.) path_resolution(7)ดูเพิ่มเติม (linux-manpages ที่เก่ากว่ามี man page ในส่วนที่ 2 เช่น die.net) IDK วิธีที่ Linux ใช้งานได้จริงภายในเพื่อปรับการตรวจสอบส่วนประกอบไดเรกทอรีทั้งหมดให้เหมาะสมที่สุด อาจตรึงรายการ VFS นั้นในแคชหรือไม่
Peter Cordes

2
ขวาที่จุดของฉัน ... ดังนั้นfs/namei.c(เส้นทาง -> inode ค้นหา) เรียกเป็น namespace.c lookup_mnt()แม้ว่า มีการตั้งค่าสถานะบน dentry (รายการแคชไดเรกทอรี) แต่นั่นเป็นเพียงการเพิ่มประสิทธิภาพหรือรายละเอียดการใช้งาน ไม่ได้บอกคุณว่าระบบไฟล์ใดติดตั้งอยู่ที่นั่น คุณต้องดูในตารางเมานต์ (ดู m_hash () เพื่อดูรายละเอียดเพิ่มเติมเกี่ยวกับการใช้งานอย่างน้อย Linux หลีกเลี่ยงการเปรียบเทียบสตริงเพิ่มเติมและ AFAICS ในเวลาเดียวกันก็สามารถใช้งาน Dentry ได้อีกครั้งเช่น bind mounts เพราะมันถูกเขียนขึ้นโดยพ่อมด)
sourcejedi

1
@PeterCordes man 8 mount:: mount --bind foo foo. การmountเรียกการเชื่อมต่อแนบ (ส่วนหนึ่งของ) ระบบไฟล์เดียวเท่านั้นไม่สามารถส่งได้ ลำดับชั้นของไฟล์ทั้งหมดรวมถึงการติดตั้งย่อยจะถูกแนบเป็นที่สองโดยใช้ :mount --rbind olddir newdir
mikeserv

19

หากmount(2) ต้องการสร้างไดเรกทอรีใหม่ให้เป็นจุดเชื่อมต่อคุณจะไม่สามารถเมานต์สิ่งใด ๆ ภายใต้ระบบไฟล์แบบอ่านอย่างเดียว นั่นจะเป็นใบ้ดังนั้นเราสามารถแยกแยะออกได้

หากการเมานต์สร้างทางเลือกไดเรกทอรีใหม่ให้เป็นจุดเมานท์นั่นจะแปลก มันไม่เหมือนการเมานท์ / unmount เกิดขึ้นตลอดเวลาดังนั้นการวางตรรกะพิเศษในเคอร์เนลเพื่อทำสองขั้นตอนเหล่านี้ด้วยการเรียกระบบครั้งเดียวจะไม่เป็นการเร่งความเร็วที่สำคัญ เพียงแค่ปล่อยให้มันขึ้นอยู่กับพื้นที่ของผู้ใช้เพื่อทำการmkdir(2)โทรออกระบบหากต้องการ คำตอบของ Dmitry ชี้ให้เห็นว่าการmount(2)ทำทั้งสองอย่างนั้นจะไม่ทำให้เกิดปรมาณู และคุณต้องการอาร์กิวเมนต์พิเศษที่จะmount(2)มีธงโหมดชอบopen(2)ใช้เวลาสำหรับO_CREAT, O_EXCLฯลฯ มันก็จะโง่เมื่อเทียบกับการปล่อยให้ผู้ใช้พื้นที่ทำมัน

หรือบางทีคุณถูกถามว่ามีmount(8)(โปรแกรมดั้งเดิมที่mount(2)เรียกใช้ระบบ) ทำเช่นนี้? นั่นอาจเป็นไปได้ แต่ก็มีประโยชน์อย่างสมบูรณ์แบบmkdir(1)สำหรับงานและการออกแบบของ Unix นั้นเป็นเครื่องมือขนาดเล็กที่ดีที่สามารถรวมเข้าด้วยกัน หากคุณต้องการเครื่องมือที่ทำทั้งสองอย่างมันเป็นเรื่องง่ายที่จะเขียนเชลล์สคริปเพื่อสร้างเครื่องมือนั้นจากสองเครื่องมือที่ง่ายกว่า (หรือตามที่ muru แสดงความคิดเห็นudisksctlได้ทำเช่นนี้แล้วดังนั้นคุณไม่จำเป็นต้องเขียน) นอกจากนี้ Linux ทั่วไปmount(8)จาก util-linux สนับสนุนการmount -o x-mount.mkdir[=mode]ใช้มันเป็นx-ไวยากรณ์สำหรับตัวเลือกสำหรับ userspace มากกว่าตัวเลือกที่จะส่งผ่านไปยังระบบไฟล์


ตอนนี้คำถามที่น่าสนใจมากขึ้น: ทำไมต้องมีไดเรกทอรีในระบบไฟล์หลักเลย?

เช่นเดียวกับคำตอบของ pjc50 ชี้ให้เห็น (ไม่มีส่วนเกี่ยวข้องแม้เขาจะมีชื่อย่อของฉัน!) readdir()มีจุดเชื่อมต่อแสดงในรายการไดเรกทอรีแล้วจะต้องมีการตรวจสอบเป็นพิเศษในทุก

มีจุดเมานท์อยู่ในไดเรกทอรีในไดเรกทอรีที่ประกอบด้วย (บนพาเรนต์ FS) เป็นกลลวงที่ดี readdir()ไม่ต้องสังเกตว่ามันเป็นจุดเมานท์เลย สิ่งนี้จะเกิดขึ้นหากจุดเชื่อมต่อถูกใช้เป็นส่วนประกอบของเส้นทาง แน่นอนว่าการแก้ปัญหาพา ธ จะต้องตรวจสอบตารางการเมานต์สำหรับทุกองค์ประกอบไดเรกทอรีของเส้นทาง


1
If mount(2) required the creation of a new directory to be the mount point, you couldn't mount anything under a read-only filesystem. That would be dumb- ฉันจะเถียงอย่างชาญฉลาด: จากมุมมองของผู้ใช้ระบบไฟล์แบบอ่านอย่างเดียวไม่ควรเปลี่ยน แต่การอนุญาตให้เมาท์หมายความว่าทำได้
Izkata

2
@Izkata: การสร้างระบบไฟล์เป็นแบบอ่านอย่างเดียวไม่ได้หมายความว่าทรีย่อยของ VFS ทั้งหมดจะถูกแช่แข็ง มันอาจจะมี symlinks ชี้ในการอ่านเขียนไดเรกทอรีหรือมีอยู่แล้วได้อ่านเขียนจุดเมาภายใต้มันเมื่อ FS roแม่ก็นั่งประจำที่ มีหลายกรณีการใช้งานสำหรับระบบไฟล์แบบอ่านอย่างเดียวที่อาร์กิวเมนต์ของคุณไม่สมเหตุสมผล
Peter Cordes

2
man 8 mount: x-mount.mkdir[=mode] อนุญาตให้สร้างไดเรกทอรีเป้าหมาย (จุดเมานท์) โหมดอาร์กิวเมนต์ที่เป็นทางเลือกระบุโหมดการเข้าถึงระบบไฟล์ที่ใช้mkdir(2)ในรูปแบบเลขฐานแปด โหมดเริ่มต้นคือ 0755 ฟังก์ชั่นนี้รองรับเฉพาะผู้ใช้รูท
mikeserv

ฉันไม่เห็นกรณีการใช้งานที่สำคัญของระบบไฟล์แบบอ่านอย่างเดียวที่มีระบบไฟล์อ่าน - เขียนติดตั้งโดยเฉพาะอย่างยิ่งไม่ได้อยู่ใน Unix ต้น @PeterCordes
kubanczyk

@kubanczyk: อ่านอย่างเดียวระบบแฟ้มรากด้วยอ่านเขียนและ/tmp /homeหรือติดตั้ง NFS แบบอ่านอย่างเดียว/usrพร้อมกับโลคัล/usr/localเมาท์บน หรือโดยทั่วไปแล้วรูปภาพแบบอ่านอย่างเดียวที่ใช้ร่วมกันซึ่งมีชิ้นส่วนที่แก้ไขได้ซึ่งประกอบอยู่เหนือ (mods ในตัวเครื่องสำหรับรูปภาพแบบอ่านอย่างเดียวยังสามารถทำได้แบบต่อไฟล์ด้วยระบบไฟล์ที่กำหนดเองเช่นoverlayfsหรือระบบไฟล์ร่วมอื่น ๆ สำหรับ Linux ที่ใช้กับอิมเมจที่บูตได้ใน LiveCD) ตอนแรกฉันนึกถึง RO FS ที่เมาท์ บูต แต่ทำให้มัน rw สามารถเกิดขึ้นได้ก่อนที่จะเมานท์อื่น ๆ
Peter Cordes

12

การติดตั้งไดเรกทอรีที่มีอยู่ทำให้การเรียกmountใช้อะตอมมิกจริง: มันประสบความสำเร็จหรือล้มเหลวอย่างน้อยจากมุมมองของผู้ใช้ หากmountต้องสร้างจุดเมานท์เองมันจะมีสองจุดของความล้มเหลวทำให้ไม่สามารถรับประกันการย้อนกลับที่สะอาดได้ ลองนึกภาพสถานการณ์ต่อไปนี้:

  1. mount สร้างจุดเมานต์สำเร็จแล้ว
  2. mount พยายามเมานต์ระบบไฟล์ใหม่ไปยังไดเร็กทอรีนั้น แต่ล้มเหลว
  3. mount พยายามลบจุดเมานท์ แต่ล้มเหลว

mountระบบจะจบลงด้วยผลข้างเคียงของล้มเหลว

นี่คืออีกหนึ่ง:

  1. umount ประสบความสำเร็จในการยกเลิกการต่อเชื่อมระบบไฟล์
  2. umount พยายามลบจุดเมานท์ แต่ล้มเหลว

ตอนนี้ควรumountกลับสู่ความสำเร็จหรือล้มเหลวหรือไม่


5
mountมี 8 รหัสส่งคืนที่แตกต่างกันสำหรับข้อผิดพลาดซึ่งสามารถรวมกันได้ มันสามารถเพิ่มอีกหนึ่งเมื่อการลบไดเรกทอรีล้มเหลว man7.org/linux/man-pages/man8/mount.8.html#RETURN_CODES
โกลาหล

8
ฉันคิดว่า OP กำลังถามว่าทำไมจุดเมานท์จำเป็นต้องเป็นไดเรกทอรีที่มีอยู่เลยไม่ใช่สาเหตุที่mountระบบไม่สร้างมันขึ้นมา แม้ว่านั่นอาจเป็นเพียงการตีความ / ความคาดหวังของฉันในสิ่งที่ฉันคิดว่า OP หมายถึงถามหรือสิ่งที่ฉันจะถามถ้าฉันถาม
Peter Cordes

3

อีกกรณีหนึ่งที่สามารถเกิดขึ้นได้:

เมื่อคุณบูตรูปภาพพื้นฐานแบบอ่านอย่างเดียวจะโหลดในไดเรกทอรีราก ดังนั้นคุณต้องการที่จะแทนที่มันเมื่อคุณต้องการที่จะกองรากจริง ดังนั้นคุณสามารถจินตนาการว่าภูเขา syscall เพียงแค่สลับจุดเมานท์ไปrorw

ที่นี่ลองนึกภาพว่าคุณมีปัญหาเกี่ยวกับระบบไฟล์ในจุดเมานท์รูทคุณต้องการที่จะลองซ่อม ด้วยการเมาท์ทับซ้อนคุณสามารถยกเลิกการต่อเชื่อมระบบไฟล์และใช้fsckเป็นรูปภาพพื้นฐานเพื่อแก้ปัญหา

คุณลักษณะนี้ยังสามารถเป็นประโยชน์ในระบบที่ต้องรักษาความปลอดภัยที่แข็งแกร่งที่จะทำให้การติดตามการเปลี่ยนแปลงระหว่างroพาร์ทิชันและrwหนึ่ง


1
ฉันไม่แน่ใจว่าวิธีนี้ตอบคำถาม คุณกำลังชี้ให้เห็นหรือไม่ว่าหากmount จำเป็นต้องสร้างไดเรกทอรีใหม่ที่ตำแหน่งของจุดเมานท์ที่คุณไม่สามารถเมาท์อะไรก็ได้บนระบบไฟล์แบบอ่านอย่างเดียว? ย่อหน้าเปิดสับสน: นั่นไม่ใช่วิธีที่ Linux เริ่มทำงาน มันใช้การpivot_rootเรียกระบบเพื่อเปลี่ยนรูท fs ไม่ใช่แค่เมานต์สิ่งอื่น ๆ pivot_root(2)ที่ทำให้มันยากที่จะทำตามตรรกะของคุณในวรรคถัดไปเพราะผมคิดว่าคุณกำลังพูดถึง
Peter Cordes

2
@PeterCordes - linux ไม่ได้ใช้ initrd เป็นเวลาหลายปี : เมื่อเปลี่ยนอุปกรณ์รูทอีกอัน initrd ก็จะทำเช่นpivot_rootนั้นและumountramdisk แต่initramfs เป็น rootfs: คุณไม่สามารถที่จะpivot_rootrootfs หรือเลิกเมานท์ แทนที่จะลบทุกอย่างออกจาก rootfs เพื่อเพิ่มพื้นที่ ( find -xdev / -exec rm {} \;), overmount rootfs ด้วย root ใหม่ ( cd /newmount; mount --move . /; chroot .), แนบ stdin / stdout / stderr ไปที่ใหม่ / dev / console และexecใหม่init
mikeserv

@mikeserv: เรียบร้อย! ฉันไม่ได้ตระหนักว่ากลไกพื้นฐานสำหรับการสลับรูทนั้นเปลี่ยนไปเมื่อเราเริ่มใช้ initramfs แทนที่จะเป็น initrd จาก "ให้แน่ใจว่าโมดูลเคอร์เนลที่ถูกต้องอยู่ในนั้น" มุมมองของผู้ดูแลระบบพวกเขาเหมือนกัน>. ฉันยังคงคิดว่ามันไม่ตอบคำถามได้ดีนัก ดูเหมือนว่าการตีความ "การติดตั้งภายใต้ rofs นั้นเป็นไปไม่ได้" และทำให้เกิดปัญหาที่เฉพาะเจาะจง (ซึ่งไม่น่าเป็นไปได้เพราะ initramfs ไม่ได้ติดตั้งแบบอ่านอย่างเดียวตอนบูต) และถึงแม้ว่ามันจะสามารถอ่านซ้ำได้ เขียนโดยไม่ส่งผลกระทบต่อภาพ cpio.gz)
Peter Cordes

@PeterCordes - ฉันไม่เข้าใจคำตอบนี้จริงๆ ฉันเพิ่งเห็นความคิดเห็นของคุณ - initramfs เป็นระบบไฟล์ - มันไม่สามารถอ่านได้อย่างเดียว - แคช fs เป็นตัวตน
mikeserv

2

ฉันก็สงสัยเช่นกัน

เสื้อคลุมเรียบง่ายเช่น:

#!/bin/sh
eval "mkdir -p \"\$$#\"" 
/bin/mount "$@"  

บันทึกเป็นสคริปต์ที่เรียกใช้งานได้ซึ่งมีชื่อmountอยู่ในไดเรกทอรีที่เขียนทับ/binใน PATH ของคุณควรดูแลสิ่งนี้หากมันรบกวนคุณมากเกินไป

(ก่อนที่จะเรียกใช้mountไบนารีจริงมันจะสร้างไดเรกทอรีที่ตั้งชื่อตามอาร์กิวเมนต์ล่าสุดmountหากไม่มีไดเรกทอรีดังกล่าวอยู่แล้ว)


อีกทางเลือกหนึ่งถ้าคุณไม่ต้องการให้ invocations ของmountwrapper สร้างไดเรคทอรีล้มเหลวคุณสามารถทำได้:

#!/bin/sh
set -e
eval "lastArg=\"\$$#\""
test -d "$lastArg" || { mkdir "$lastArg"; madeDir=1; }
/bin/mount "$@"  ||  {  test -z "$madeDir" || rmdir "$lastArg"; }

mountคำสั่งไม่ควรใช้ไดเรกทอรีที่สร้างขึ้นดังนั้น?
muru

1
@muru นั่นคือสิ่งที่บรรทัดสุดท้ายทำ
PSkocik

โอ้คุณหมายความว่ามันควรจะใช้: mount /dev/foo /some/path? ฉันคิดว่ามันจะทำงานเหมือนไม่ดังนั้นคุณจะทำงานudisksctl mount /dev/foo
muru

4
คุณจะได้รับหาเรื่อง cmdline ที่ผ่านมาโดยไม่ได้evalที่จะขยายการใช้$# "${@:-1}"ฉันทดสอบสิ่งนี้ด้วย DASH เนื่องจากฉันคิดว่ามันไม่รองรับอะไรเกินกว่าที่ POSIX sh ต้องการเพื่อสนับสนุน พิมพ์/bin/dash -c 'echo ${@:-1}' foo bar bar
Peter Cordes

1
คุณสามารถใช้man -o x-mount.mkdir...
mikeserv
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.