@@ -69,7 +69,10 @@ func getFreeLoopDev() (uint32, error) {
6969 return uint32 (num ), nil
7070}
7171
72- func setupLoopDev (backingFile , loopDev string , param LoopParams ) error {
72+ // setupLoopDev attaches the backing file to the loop device and returns
73+ // the file handle for the loop device. The caller is responsible for
74+ // closing the file handle.
75+ func setupLoopDev (backingFile , loopDev string , param LoopParams ) (* os.File , error ) {
7376 // 1. Open backing file and loop device
7477 flags := os .O_RDWR
7578 if param .Readonly {
@@ -78,19 +81,20 @@ func setupLoopDev(backingFile, loopDev string, param LoopParams) error {
7881
7982 back , err := os .OpenFile (backingFile , flags , 0 )
8083 if err != nil {
81- return errors .Wrapf (err , "could not open backing file: %s" , backingFile )
84+ return nil , errors .Wrapf (err , "could not open backing file: %s" , backingFile )
8285 }
8386 defer back .Close ()
8487
88+ // To make Autoclear (LO_FLAGS_AUTOCLEAR) work, this function intentionally
89+ // doesn't close this file handle on defer.
8590 loop , err := os .OpenFile (loopDev , flags , 0 )
8691 if err != nil {
87- return errors .Wrapf (err , "could not open loop device: %s" , loopDev )
92+ return nil , errors .Wrapf (err , "could not open loop device: %s" , loopDev )
8893 }
89- defer loop .Close ()
9094
9195 // 2. Set FD
9296 if _ , _ , err = ioctl (loop .Fd (), unix .LOOP_SET_FD , back .Fd ()); err != nil {
93- return errors .Wrapf (err , "could not set loop fd for device: %s" , loopDev )
97+ return nil , errors .Wrapf (err , "could not set loop fd for device: %s" , loopDev )
9498 }
9599
96100 // 3. Set Info
@@ -110,7 +114,7 @@ func setupLoopDev(backingFile, loopDev string, param LoopParams) error {
110114
111115 _ , _ , err = ioctl (loop .Fd (), unix .LOOP_SET_STATUS64 , uintptr (unsafe .Pointer (& info )))
112116 if err == nil {
113- return nil
117+ return loop , nil
114118 }
115119
116120 if param .Direct {
@@ -119,13 +123,16 @@ func setupLoopDev(backingFile, loopDev string, param LoopParams) error {
119123 info .Flags &= ^ (uint32 (unix .LO_FLAGS_DIRECT_IO ))
120124 _ , _ , err = ioctl (loop .Fd (), unix .LOOP_SET_STATUS64 , uintptr (unsafe .Pointer (& info )))
121125 if err == nil {
122- return nil
126+ return loop , nil
123127 }
124128 }
125129
126- // Cleanup loop fd and return error
130+ // Cleanup loop fd and return error.
131+ // If this function doesn't return this file handle, it must close the handle to
132+ // prevent file descriptor leaks.
133+ loop .Close ()
127134 _ , _ , _ = ioctl (loop .Fd (), unix .LOOP_CLR_FD , 0 )
128- return errors .Errorf ("failed to set loop device info: %v" , err )
135+ return nil , errors .Errorf ("failed to set loop device info: %v" , err )
129136}
130137
131138// setupLoop looks for (and possibly creates) a free loop device, and
@@ -142,15 +149,16 @@ func setupLoopDev(backingFile, loopDev string, param LoopParams) error {
142149// the loop device when done with it.
143150//
144151// Upon success, the file handle to the loop device is returned.
145- func setupLoop (backingFile string , param LoopParams ) (string , error ) {
152+ func setupLoop (backingFile string , param LoopParams ) (* os. File , error ) {
146153 for retry := 1 ; retry < 100 ; retry ++ {
147154 num , err := getFreeLoopDev ()
148155 if err != nil {
149- return "" , err
156+ return nil , err
150157 }
151158
152159 loopDev := fmt .Sprintf (loopDevFormat , num )
153- if err := setupLoopDev (backingFile , loopDev , param ); err != nil {
160+ file , err := setupLoopDev (backingFile , loopDev , param )
161+ if err != nil {
154162 // Per util-linux/sys-utils/losetup.c:create_loop(),
155163 // free loop device can race and we end up failing
156164 // with EBUSY when trying to set it up.
@@ -159,13 +167,13 @@ func setupLoop(backingFile string, param LoopParams) (string, error) {
159167 time .Sleep (time .Millisecond * time .Duration (rand .Intn (retry * 10 )))
160168 continue
161169 }
162- return "" , err
170+ return nil , err
163171 }
164172
165- return loopDev , nil
173+ return file , nil
166174 }
167175
168- return "" , errors .New ("timeout creating new loopback device" )
176+ return nil , errors .New ("timeout creating new loopback device" )
169177}
170178
171179func removeLoop (loopdev string ) error {
@@ -181,7 +189,12 @@ func removeLoop(loopdev string) error {
181189
182190// Attach a specified backing file to a loop device
183191func AttachLoopDevice (backingFile string ) (string , error ) {
184- return setupLoop (backingFile , LoopParams {})
192+ file , err := setupLoop (backingFile , LoopParams {})
193+ if err != nil {
194+ return "" , err
195+ }
196+ defer file .Close ()
197+ return file .Name (), nil
185198}
186199
187200// Detach a loop device
0 commit comments