~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/fs/hfsplus/dir.c

Version: ~ [ linux-5.13-rc1 ] ~ [ linux-5.12.2 ] ~ [ linux-5.11.19 ] ~ [ linux-5.10.35 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.117 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.190 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.232 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.268 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.268 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  2 /*
  3  *  linux/fs/hfsplus/dir.c
  4  *
  5  * Copyright (C) 2001
  6  * Brad Boyer (flar@allandria.com)
  7  * (C) 2003 Ardis Technologies <roman@ardistech.com>
  8  *
  9  * Handling of directories
 10  */
 11 
 12 #include <linux/errno.h>
 13 #include <linux/fs.h>
 14 #include <linux/slab.h>
 15 #include <linux/random.h>
 16 #include <linux/nls.h>
 17 
 18 #include "hfsplus_fs.h"
 19 #include "hfsplus_raw.h"
 20 #include "xattr.h"
 21 
 22 static inline void hfsplus_instantiate(struct dentry *dentry,
 23                                        struct inode *inode, u32 cnid)
 24 {
 25         dentry->d_fsdata = (void *)(unsigned long)cnid;
 26         d_instantiate(dentry, inode);
 27 }
 28 
 29 /* Find the entry inside dir named dentry->d_name */
 30 static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
 31                                      unsigned int flags)
 32 {
 33         struct inode *inode = NULL;
 34         struct hfs_find_data fd;
 35         struct super_block *sb;
 36         hfsplus_cat_entry entry;
 37         int err;
 38         u32 cnid, linkid = 0;
 39         u16 type;
 40 
 41         sb = dir->i_sb;
 42 
 43         dentry->d_fsdata = NULL;
 44         err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
 45         if (err)
 46                 return ERR_PTR(err);
 47         err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino,
 48                         &dentry->d_name);
 49         if (unlikely(err < 0))
 50                 goto fail;
 51 again:
 52         err = hfs_brec_read(&fd, &entry, sizeof(entry));
 53         if (err) {
 54                 if (err == -ENOENT) {
 55                         hfs_find_exit(&fd);
 56                         /* No such entry */
 57                         inode = NULL;
 58                         goto out;
 59                 }
 60                 goto fail;
 61         }
 62         type = be16_to_cpu(entry.type);
 63         if (type == HFSPLUS_FOLDER) {
 64                 if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) {
 65                         err = -EIO;
 66                         goto fail;
 67                 }
 68                 cnid = be32_to_cpu(entry.folder.id);
 69                 dentry->d_fsdata = (void *)(unsigned long)cnid;
 70         } else if (type == HFSPLUS_FILE) {
 71                 if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
 72                         err = -EIO;
 73                         goto fail;
 74                 }
 75                 cnid = be32_to_cpu(entry.file.id);
 76                 if (entry.file.user_info.fdType ==
 77                                 cpu_to_be32(HFSP_HARDLINK_TYPE) &&
 78                                 entry.file.user_info.fdCreator ==
 79                                 cpu_to_be32(HFSP_HFSPLUS_CREATOR) &&
 80                                 HFSPLUS_SB(sb)->hidden_dir &&
 81                                 (entry.file.create_date ==
 82                                         HFSPLUS_I(HFSPLUS_SB(sb)->hidden_dir)->
 83                                                 create_date ||
 84                                 entry.file.create_date ==
 85                                         HFSPLUS_I(d_inode(sb->s_root))->
 86                                                 create_date)) {
 87                         struct qstr str;
 88                         char name[32];
 89 
 90                         if (dentry->d_fsdata) {
 91                                 /*
 92                                  * We found a link pointing to another link,
 93                                  * so ignore it and treat it as regular file.
 94                                  */
 95                                 cnid = (unsigned long)dentry->d_fsdata;
 96                                 linkid = 0;
 97                         } else {
 98                                 dentry->d_fsdata = (void *)(unsigned long)cnid;
 99                                 linkid =
100                                         be32_to_cpu(entry.file.permissions.dev);
101                                 str.len = sprintf(name, "iNode%d", linkid);
102                                 str.name = name;
103                                 err = hfsplus_cat_build_key(sb, fd.search_key,
104                                         HFSPLUS_SB(sb)->hidden_dir->i_ino,
105                                         &str);
106                                 if (unlikely(err < 0))
107                                         goto fail;
108                                 goto again;
109                         }
110                 } else if (!dentry->d_fsdata)
111                         dentry->d_fsdata = (void *)(unsigned long)cnid;
112         } else {
113                 pr_err("invalid catalog entry type in lookup\n");
114                 err = -EIO;
115                 goto fail;
116         }
117         hfs_find_exit(&fd);
118         inode = hfsplus_iget(dir->i_sb, cnid);
119         if (IS_ERR(inode))
120                 return ERR_CAST(inode);
121         if (S_ISREG(inode->i_mode))
122                 HFSPLUS_I(inode)->linkid = linkid;
123 out:
124         return d_splice_alias(inode, dentry);
125 fail:
126         hfs_find_exit(&fd);
127         return ERR_PTR(err);
128 }
129 
130 static int hfsplus_readdir(struct file *file, struct dir_context *ctx)
131 {
132         struct inode *inode = file_inode(file);
133         struct super_block *sb = inode->i_sb;
134         int len, err;
135         char *strbuf;
136         hfsplus_cat_entry entry;
137         struct hfs_find_data fd;
138         struct hfsplus_readdir_data *rd;
139         u16 type;
140 
141         if (file->f_pos >= inode->i_size)
142                 return 0;
143 
144         err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
145         if (err)
146                 return err;
147         strbuf = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN + 1, GFP_KERNEL);
148         if (!strbuf) {
149                 err = -ENOMEM;
150                 goto out;
151         }
152         hfsplus_cat_build_key_with_cnid(sb, fd.search_key, inode->i_ino);
153         err = hfs_brec_find(&fd, hfs_find_rec_by_key);
154         if (err)
155                 goto out;
156 
157         if (ctx->pos == 0) {
158                 /* This is completely artificial... */
159                 if (!dir_emit_dot(file, ctx))
160                         goto out;
161                 ctx->pos = 1;
162         }
163         if (ctx->pos == 1) {
164                 if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
165                         err = -EIO;
166                         goto out;
167                 }
168 
169                 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
170                         fd.entrylength);
171                 if (be16_to_cpu(entry.type) != HFSPLUS_FOLDER_THREAD) {
172                         pr_err("bad catalog folder thread\n");
173                         err = -EIO;
174                         goto out;
175                 }
176                 if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) {
177                         pr_err("truncated catalog thread\n");
178                         err = -EIO;
179                         goto out;
180                 }
181                 if (!dir_emit(ctx, "..", 2,
182                             be32_to_cpu(entry.thread.parentID), DT_DIR))
183                         goto out;
184                 ctx->pos = 2;
185         }
186         if (ctx->pos >= inode->i_size)
187                 goto out;
188         err = hfs_brec_goto(&fd, ctx->pos - 1);
189         if (err)
190                 goto out;
191         for (;;) {
192                 if (be32_to_cpu(fd.key->cat.parent) != inode->i_ino) {
193                         pr_err("walked past end of dir\n");
194                         err = -EIO;
195                         goto out;
196                 }
197 
198                 if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
199                         err = -EIO;
200                         goto out;
201                 }
202 
203                 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
204                         fd.entrylength);
205                 type = be16_to_cpu(entry.type);
206                 len = NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN;
207                 err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len);
208                 if (err)
209                         goto out;
210                 if (type == HFSPLUS_FOLDER) {
211                         if (fd.entrylength <
212                                         sizeof(struct hfsplus_cat_folder)) {
213                                 pr_err("small dir entry\n");
214                                 err = -EIO;
215                                 goto out;
216                         }
217                         if (HFSPLUS_SB(sb)->hidden_dir &&
218                             HFSPLUS_SB(sb)->hidden_dir->i_ino ==
219                                         be32_to_cpu(entry.folder.id))
220                                 goto next;
221                         if (!dir_emit(ctx, strbuf, len,
222                                     be32_to_cpu(entry.folder.id), DT_DIR))
223                                 break;
224                 } else if (type == HFSPLUS_FILE) {
225                         u16 mode;
226                         unsigned type = DT_UNKNOWN;
227 
228                         if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
229                                 pr_err("small file entry\n");
230                                 err = -EIO;
231                                 goto out;
232                         }
233 
234                         mode = be16_to_cpu(entry.file.permissions.mode);
235                         if (S_ISREG(mode))
236                                 type = DT_REG;
237                         else if (S_ISLNK(mode))
238                                 type = DT_LNK;
239                         else if (S_ISFIFO(mode))
240                                 type = DT_FIFO;
241                         else if (S_ISCHR(mode))
242                                 type = DT_CHR;
243                         else if (S_ISBLK(mode))
244                                 type = DT_BLK;
245                         else if (S_ISSOCK(mode))
246                                 type = DT_SOCK;
247 
248                         if (!dir_emit(ctx, strbuf, len,
249                                       be32_to_cpu(entry.file.id), type))
250                                 break;
251                 } else {
252                         pr_err("bad catalog entry type\n");
253                         err = -EIO;
254                         goto out;
255                 }
256 next:
257                 ctx->pos++;
258                 if (ctx->pos >= inode->i_size)
259                         goto out;
260                 err = hfs_brec_goto(&fd, 1);
261                 if (err)
262                         goto out;
263         }
264         rd = file->private_data;
265         if (!rd) {
266                 rd = kmalloc(sizeof(struct hfsplus_readdir_data), GFP_KERNEL);
267                 if (!rd) {
268                         err = -ENOMEM;
269                         goto out;
270                 }
271                 file->private_data = rd;
272                 rd->file = file;
273                 spin_lock(&HFSPLUS_I(inode)->open_dir_lock);
274                 list_add(&rd->list, &HFSPLUS_I(inode)->open_dir_list);
275                 spin_unlock(&HFSPLUS_I(inode)->open_dir_lock);
276         }
277         /*
278          * Can be done after the list insertion; exclusion with
279          * hfsplus_delete_cat() is provided by directory lock.
280          */
281         memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key));
282 out:
283         kfree(strbuf);
284         hfs_find_exit(&fd);
285         return err;
286 }
287 
288 static int hfsplus_dir_release(struct inode *inode, struct file *file)
289 {
290         struct hfsplus_readdir_data *rd = file->private_data;
291         if (rd) {
292                 spin_lock(&HFSPLUS_I(inode)->open_dir_lock);
293                 list_del(&rd->list);
294                 spin_unlock(&HFSPLUS_I(inode)->open_dir_lock);
295                 kfree(rd);
296         }
297         return 0;
298 }
299 
300 static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
301                         struct dentry *dst_dentry)
302 {
303         struct hfsplus_sb_info *sbi = HFSPLUS_SB(dst_dir->i_sb);
304         struct inode *inode = d_inode(src_dentry);
305         struct inode *src_dir = d_inode(src_dentry->d_parent);
306         struct qstr str;
307         char name[32];
308         u32 cnid, id;
309         int res;
310 
311         if (HFSPLUS_IS_RSRC(inode))
312                 return -EPERM;
313         if (!S_ISREG(inode->i_mode))
314                 return -EPERM;
315 
316         mutex_lock(&sbi->vh_mutex);
317         if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) {
318                 for (;;) {
319                         get_random_bytes(&id, sizeof(cnid));
320                         id &= 0x3fffffff;
321                         str.name = name;
322                         str.len = sprintf(name, "iNode%d", id);
323                         res = hfsplus_rename_cat(inode->i_ino,
324                                                  src_dir, &src_dentry->d_name,
325                                                  sbi->hidden_dir, &str);
326                         if (!res)
327                                 break;
328                         if (res != -EEXIST)
329                                 goto out;
330                 }
331                 HFSPLUS_I(inode)->linkid = id;
332                 cnid = sbi->next_cnid++;
333                 src_dentry->d_fsdata = (void *)(unsigned long)cnid;
334                 res = hfsplus_create_cat(cnid, src_dir,
335                         &src_dentry->d_name, inode);
336                 if (res)
337                         /* panic? */
338                         goto out;
339                 sbi->file_count++;
340         }
341         cnid = sbi->next_cnid++;
342         res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode);
343         if (res)
344                 goto out;
345 
346         inc_nlink(inode);
347         hfsplus_instantiate(dst_dentry, inode, cnid);
348         ihold(inode);
349         inode->i_ctime = current_time(inode);
350         mark_inode_dirty(inode);
351         sbi->file_count++;
352         hfsplus_mark_mdb_dirty(dst_dir->i_sb);
353 out:
354         mutex_unlock(&sbi->vh_mutex);
355         return res;
356 }
357 
358 static int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
359 {
360         struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
361         struct inode *inode = d_inode(dentry);
362         struct qstr str;
363         char name[32];
364         u32 cnid;
365         int res;
366 
367         if (HFSPLUS_IS_RSRC(inode))
368                 return -EPERM;
369 
370         mutex_lock(&sbi->vh_mutex);
371         cnid = (u32)(unsigned long)dentry->d_fsdata;
372         if (inode->i_ino == cnid &&
373             atomic_read(&HFSPLUS_I(inode)->opencnt)) {
374                 str.name = name;
375                 str.len = sprintf(name, "temp%lu", inode->i_ino);
376                 res = hfsplus_rename_cat(inode->i_ino,
377                                          dir, &dentry->d_name,
378                                          sbi->hidden_dir, &str);
379                 if (!res) {
380                         inode->i_flags |= S_DEAD;
381                         drop_nlink(inode);
382                 }
383                 goto out;
384         }
385         res = hfsplus_delete_cat(cnid, dir, &dentry->d_name);
386         if (res)
387                 goto out;
388 
389         if (inode->i_nlink > 0)
390                 drop_nlink(inode);
391         if (inode->i_ino == cnid)
392                 clear_nlink(inode);
393         if (!inode->i_nlink) {
394                 if (inode->i_ino != cnid) {
395                         sbi->file_count--;
396                         if (!atomic_read(&HFSPLUS_I(inode)->opencnt)) {
397                                 res = hfsplus_delete_cat(inode->i_ino,
398                                                          sbi->hidden_dir,
399                                                          NULL);
400                                 if (!res)
401                                         hfsplus_delete_inode(inode);
402                         } else
403                                 inode->i_flags |= S_DEAD;
404                 } else
405                         hfsplus_delete_inode(inode);
406         } else
407                 sbi->file_count--;
408         inode->i_ctime = current_time(inode);
409         mark_inode_dirty(inode);
410 out:
411         mutex_unlock(&sbi->vh_mutex);
412         return res;
413 }
414 
415 static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry)
416 {
417         struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
418         struct inode *inode = d_inode(dentry);
419         int res;
420 
421         if (inode->i_size != 2)
422                 return -ENOTEMPTY;
423 
424         mutex_lock(&sbi->vh_mutex);
425         res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
426         if (res)
427                 goto out;
428         clear_nlink(inode);
429         inode->i_ctime = current_time(inode);
430         hfsplus_delete_inode(inode);
431         mark_inode_dirty(inode);
432 out:
433         mutex_unlock(&sbi->vh_mutex);
434         return res;
435 }
436 
437 static int hfsplus_symlink(struct inode *dir, struct dentry *dentry,
438                            const char *symname)
439 {
440         struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
441         struct inode *inode;
442         int res = -ENOMEM;
443 
444         mutex_lock(&sbi->vh_mutex);
445         inode = hfsplus_new_inode(dir->i_sb, dir, S_IFLNK | S_IRWXUGO);
446         if (!inode)
447                 goto out;
448 
449         res = page_symlink(inode, symname, strlen(symname) + 1);
450         if (res)
451                 goto out_err;
452 
453         res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
454         if (res)
455                 goto out_err;
456 
457         res = hfsplus_init_security(inode, dir, &dentry->d_name);
458         if (res == -EOPNOTSUPP)
459                 res = 0; /* Operation is not supported. */
460         else if (res) {
461                 /* Try to delete anyway without error analysis. */
462                 hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
463                 goto out_err;
464         }
465 
466         hfsplus_instantiate(dentry, inode, inode->i_ino);
467         mark_inode_dirty(inode);
468         goto out;
469 
470 out_err:
471         clear_nlink(inode);
472         hfsplus_delete_inode(inode);
473         iput(inode);
474 out:
475         mutex_unlock(&sbi->vh_mutex);
476         return res;
477 }
478 
479 static int hfsplus_mknod(struct inode *dir, struct dentry *dentry,
480                          umode_t mode, dev_t rdev)
481 {
482         struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
483         struct inode *inode;
484         int res = -ENOMEM;
485 
486         mutex_lock(&sbi->vh_mutex);
487         inode = hfsplus_new_inode(dir->i_sb, dir, mode);
488         if (!inode)
489                 goto out;
490 
491         if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode))
492                 init_special_inode(inode, mode, rdev);
493 
494         res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
495         if (res)
496                 goto failed_mknod;
497 
498         res = hfsplus_init_security(inode, dir, &dentry->d_name);
499         if (res == -EOPNOTSUPP)
500                 res = 0; /* Operation is not supported. */
501         else if (res) {
502                 /* Try to delete anyway without error analysis. */
503                 hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
504                 goto failed_mknod;
505         }
506 
507         hfsplus_instantiate(dentry, inode, inode->i_ino);
508         mark_inode_dirty(inode);
509         goto out;
510 
511 failed_mknod:
512         clear_nlink(inode);
513         hfsplus_delete_inode(inode);
514         iput(inode);
515 out:
516         mutex_unlock(&sbi->vh_mutex);
517         return res;
518 }
519 
520 static int hfsplus_create(struct inode *dir, struct dentry *dentry, umode_t mode,
521                           bool excl)
522 {
523         return hfsplus_mknod(dir, dentry, mode, 0);
524 }
525 
526 static int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
527 {
528         return hfsplus_mknod(dir, dentry, mode | S_IFDIR, 0);
529 }
530 
531 static int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry,
532                           struct inode *new_dir, struct dentry *new_dentry,
533                           unsigned int flags)
534 {
535         int res;
536 
537         if (flags & ~RENAME_NOREPLACE)
538                 return -EINVAL;
539 
540         /* Unlink destination if it already exists */
541         if (d_really_is_positive(new_dentry)) {
542                 if (d_is_dir(new_dentry))
543                         res = hfsplus_rmdir(new_dir, new_dentry);
544                 else
545                         res = hfsplus_unlink(new_dir, new_dentry);
546                 if (res)
547                         return res;
548         }
549 
550         res = hfsplus_rename_cat((u32)(unsigned long)old_dentry->d_fsdata,
551                                  old_dir, &old_dentry->d_name,
552                                  new_dir, &new_dentry->d_name);
553         if (!res)
554                 new_dentry->d_fsdata = old_dentry->d_fsdata;
555         return res;
556 }
557 
558 const struct inode_operations hfsplus_dir_inode_operations = {
559         .lookup                 = hfsplus_lookup,
560         .create                 = hfsplus_create,
561         .link                   = hfsplus_link,
562         .unlink                 = hfsplus_unlink,
563         .mkdir                  = hfsplus_mkdir,
564         .rmdir                  = hfsplus_rmdir,
565         .symlink                = hfsplus_symlink,
566         .mknod                  = hfsplus_mknod,
567         .rename                 = hfsplus_rename,
568         .getattr                = hfsplus_getattr,
569         .listxattr              = hfsplus_listxattr,
570 };
571 
572 const struct file_operations hfsplus_dir_operations = {
573         .fsync          = hfsplus_file_fsync,
574         .read           = generic_read_dir,
575         .iterate_shared = hfsplus_readdir,
576         .unlocked_ioctl = hfsplus_ioctl,
577         .llseek         = generic_file_llseek,
578         .release        = hfsplus_dir_release,
579 };
580 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp